//
//  GSModelsManager.m
//  AgileNYC
//
//  Created by German on 30.05.14.
//  Copyright (c) 2014 Softlegion. All rights reserved.
//

#import "SGSModelsManager.h"
#import "SGSAppConsts.h"
#import "SGSServerManager.h"
#import "SGSUtils.h"
#import "SGSAppDelegate.h"
#import "UIImageView+AFNetworking.h"
#import "RKDotNetDateFormatter.h"

#import "GSFacebook.h"
#import "GSGooglePlus.h"
#import "GSLinkedIn.h"

NSString * const syncFutureEventsListDidUpdateNotification = @"syncFutureEventsListDidUpdateNotification"; //Internal for the class use only
NSString * const syncBackEventsListDidUpdateNotification = @"syncBackEventsListDidUpdateNotification"; //Internal for the class use only
NSString * const syncContactsListDidUpdateNotification = @"syncContactsListDidUpdateNotification";  //Internal for the class

NSString * const eventsWillRenewNotification = @"eventsWillRenewNotification"; //Public notifications: all events will be deleted and uploaded from server
NSString * const eventsDidRenewNotification = @"eventsDidRenewNotification"; //Public notifications: all events will deleted and uploaded from server
NSString * const eventsDidNotRenewNotification = @"eventsDidNotRenewNotification"; //Public notifications: the events were not uploaded from server by reason of errors or else

NSString * const contactsWillRenewNotification = @"contactsWillRenewNotification"; //Public notifications: all contacts will be deleted and uploaded from server
NSString * const contactsDidRenewNotification = @"contactsDidRenewNotification"; //Public notifications: all contacts deleted and uploaded from server
NSString * const contactsDidNotRenewNotification = @"contactsDidNotRenewNotification"; //Public notifications: the contacts were not uploaded by reason of errors or else

NSString * const profileWillUpdateNotification = @"profileWillUpdateNotification"; //Public notifications: current profile will be updated only from server (not deleted)
NSString * const profileDidUpdateNotification = @"profileDidUpdateNotification"; //Public notifications: current profile updated only from server (not deleted)
NSString * const profileDidNotUpdateNotification = @"profileDidNotUpdateNotification"; //Public notifications: something wrong happened

NSString * const profileImageWillUpdateNotification = @"profileImageWillUpdateNotification"; //Public notifications: current profile will be updated only from server (not deleted)
NSString * const profileImageDidUpdateNotification = @"profileImageDidUpdateNotification"; //Public notifications: current profile updated only from server (not deleted)
NSString * const profileImageDidNotUpdateNotification = @"profileImageDidNotUpdateNotification"; //Public notifications: something wrong happened

NSString * const contactImageWillUpdateNotification = @"contactImageWillUpdateNotification"; //Public notifications: current profile will be updated only from server (not deleted)
NSString * const contactImageDidUpdateNotification = @"contactImageDidUpdateNotification"; //Public notifications: current profile updated only from server (not deleted)
NSString * const contactImageDidNotUpdateNotification = @"contactImageDidNotUpdateNotification"; //Public notifications: something wrong happened

NSString * const contactImageUpdateContactIdKey = @"contactImageUpdateContactIdKey";
NSString * const eventImageUpdateEventIdKey = @"eventImageUpdateEventIdKey";

NSString * const eventsWillUpdateNotification = @"eventsWillUpdateNotification"; //Public notifications: all current events will be updated only from server (not deleted)
NSString * const eventsDidUpdateNotification = @"eventsDidUpdateNotification"; //Public notifications all current events updated only from server (not deleted)
NSString * const eventsDidNotUpdateNotification = @"eventsDidNotUpdateNotification"; //Public notifications

NSString * const contactsWillUpdateNotification = @"contactsWillUpdateNotification"; //Public notifications
NSString * const contactsDidUpdateNotification = @"contactsDidUpdateNotification"; //Public notifications
NSString * const contactsDidNotUpdateNotification = @"contactsDidNotUpdateNotification"; //Public notifications


// Hidden properties
@interface SGSModelsManager ()

@property (strong, nonatomic) UIImageView * photoImageView;

@property (assign,nonatomic) BOOL isFutureEventsDictUpdated;
@property (assign,nonatomic) BOOL isBackEventsDictUpdated;

@property (strong, nonatomic) NSMutableDictionary * dictOfAllFutureEvents;
@property (strong, nonatomic) NSMutableDictionary * dictOfAllBackEvents;

@property (strong, nonatomic) NSMutableDictionary * dictOfAllContacts;

@property (strong, nonatomic) GSGooglePlus * googlePlus;
@property (strong, nonatomic) GSFacebook * facebook;
@property (strong, nonatomic) GSLinkedIn * linkedIn;

@property (assign, nonatomic) BOOL isCheckingConnection;

@property (strong, nonatomic) NSLock * checkConnectionLock;
@property (strong, nonatomic) NSLock * updateContactsLock;
@property (strong, nonatomic) NSLock * updateEventsLock;

@end


@implementation SGSModelsManager

@synthesize defaultManagedObjectContext = _defaultManagedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
@synthesize currentUser = _currentUser;
@synthesize currentEvent = _currentEvent;
@synthesize currentContact = _currentContact;
@synthesize arrayOfContacts = _arrayOfContacts;
@synthesize arrayOfEvents = _arrayOfEvents;


+(SGSModelsManager*) sharedModelsManager {
    static SGSModelsManager * manager = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[SGSModelsManager alloc] init];
    });
    
    return manager;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _currentUser = nil;
        _currentEvent = nil;
        _currentContact = nil;
        
        _arrayOfEvents = nil;
        _arrayOfContacts = nil;
        
        //self.controllerToBeOpened = nil;
        self.isCheckingConnection = NO;
        
#if CLEAR_DATABASE
        [self deleteAllEventsFromLocalDB];
        [self deleteAllUsersFromLocalDB];
#endif
        
        self.dictOfAllFutureEvents = [[NSMutableDictionary alloc] init];
        self.dictOfAllBackEvents = [[NSMutableDictionary alloc] init];
        
        self.dictOfAllContacts = [[NSMutableDictionary alloc] init];
        
        self.checkConnectionLock = [[NSLock alloc] init];
        self.updateContactsLock = [[NSLock alloc] init];
        self.updateEventsLock = [[NSLock alloc] init];
    }
    return self;
}

- (void)dealloc
{
    self.dictOfAllFutureEvents = nil;
    self.dictOfAllBackEvents = nil;
    self.dictOfAllContacts = nil;
    
    self.checkConnectionLock = nil;
    self.updateContactsLock = nil;
    self.updateEventsLock = nil;
}

#pragma mark - Synchronization

-(void) synchronizeDB {
    
    //For test
    /*double delayInSeconds = 2;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW , (int64_t)(delayInSeconds * NSEC_PER_SEC ));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void ) {
        for (int i=0; i<=100; i++) {
            //[self performSelectorInBackground: @selector(createContactInLocalDB) withObject: nil];
            //[self createContactInLocalDB];
            [self performSelectorInBackground: @selector(createEventInLocalDB) withObject: nil];
            //[self performSelectorInBackground: @selector(saveLocalDB) withObject: nil];
            [self performSelectorInBackground: @selector(createContactInLocalDB) withObject: nil];
            
            //dispatch_queue_t queue = dispatch_queue_create("uz.softlegion.agileNYC.insertObject", NULL);;
            
            dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );
            
            dispatch_async(queue1, ^{
                NSArray * users = [self executeRequestToLocalDB:@"GSUser" condition:@"isMyProfile = 0" orderByArray:nil];
            });

            dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );
            
            dispatch_async(queue2, ^{
                NSArray * events = [self executeRequestToLocalDB:@"GSEvent" condition:nil orderByArray:@[@"startTime"]];
            });

            //[self performSelectorInBackground: @selector(saveLocalDB) withObject: nil];
            //[self saveLocalDB];
        }
    });*/

    double delayInSeconds = SYNCHRONIZATION_DELAY;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW , (int64_t)(delayInSeconds * NSEC_PER_SEC ));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void ) {
        
        GSLog(GSLogEvent, @"Syncronization started");
        @autoreleasepool {
            [self synchronizeMyProfile];
            [self synchronizeEventsStep1];
            [self synchronizeContactsStep1];
        }
    });
}

-(void) synchronizeEventsStep1 {
    if (!_arrayOfEvents) {
        return;
    }
    
    if ([_arrayOfEvents count] == 0) {
        //return;
    }
    
    if (![self.updateEventsLock tryLock]) {
        return;
    }
    
    [self startStatusSpinner];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(synchronizeEventsStep2)
                                                 name:syncFutureEventsListDidUpdateNotification
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(synchronizeEventsStep2)
                                                 name:syncBackEventsListDidUpdateNotification
                                               object:nil];

    self.isFutureEventsDictUpdated = NO;
    self.isBackEventsDictUpdated = NO;
    
    [[NSNotificationCenter defaultCenter] postNotificationName:eventsWillUpdateNotification object:nil];
    
    [self loadAllFutureEventsListFromServer:YES];
    [self loadAllBackEventsListFromServer:YES];

    
}

-(void) synchronizeEventsStep2 {
    
    BOOL theListOfEventsIsChanged = NO;
    
    if (!(self.isFutureEventsDictUpdated && self.isBackEventsDictUpdated)) {
        return; //Wait when all list are updated
    }
    
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:syncFutureEventsListDidUpdateNotification
                                                  object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:syncBackEventsListDidUpdateNotification
                                                  object:nil];
    
    RKDotNetDateFormatter * dtFormat = [RKDotNetDateFormatter dotNetDateFormatterWithTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
    
    // Merge past and futures events
    NSMutableDictionary * dictOfAllEvents = [NSMutableDictionary dictionaryWithDictionary:self.dictOfAllFutureEvents];
    [dictOfAllEvents addEntriesFromDictionary:self.dictOfAllBackEvents];
    
    GSLog(GSLogInfo, @"Events in the local DB: %d, Past events on the server: %d, Future events on the server: %d",
                                                                                            [_arrayOfEvents count],
                                                                                    [self.dictOfAllBackEvents count],
                                                                                    [self.dictOfAllFutureEvents count]);
    
    
    if ([dictOfAllEvents count]==0) {

        GSLog(GSLogWarning, @"There are no events on the server. Synchronization skipped");
        [[NSNotificationCenter defaultCenter] postNotificationName:eventsDidNotUpdateNotification object:nil];
        
    } else {
        
        @synchronized(self) {
            for (GSEvent * event in _arrayOfEvents) {
                NSDictionary * dict = [dictOfAllEvents objectForKey:event.eventId];
                
                if (!dict) {
                    [self deleteObjectInEntity:event];
                    theListOfEventsIsChanged = YES;
                    
                    GSLog(GSLogWarning, @"[syncrhonizeEvents] The new event %@ has to be deleted from local DB", event.topic);
                    
                } else {
                    if ([event.isRatingChanged boolValue]) {
                        [self sendEventRateToServer:event onComplete:nil];
                    }
                    
                    if ([event.isCheckInChanged boolValue]) {
                        [self sendCheckInToServerForEvent:event onComplete:nil];
                    }
                }
            }
        }
                
        @synchronized(self) {
            
            for (NSDictionary * dict in [dictOfAllEvents allValues]) {
                
                NSNumber * eventIdFromServer = [dict objectForKey:eventIDKey];
                
                GSEvent * eventFromLocalDB = [self eventByID:eventIdFromServer];
                
                if (!eventFromLocalDB) {
                    GSEvent * newEvent = [self createEventInLocalDB];
                    
                    [newEvent completeWithServerResponse:dict];
                    
                    GSLog(GSLogInfo, @"[syncrhonizeEvents] The new event %@ has to be saved in local DB", newEvent.topic);
                    
                    [self saveLocalDB];
                    
                    theListOfEventsIsChanged = YES;
                    
                } else {
                    
                    NSDate * dateOfEventFromServer = [dtFormat dateFromString:[dict objectForKey:modifiedDateKey]];
                    
                    NSComparisonResult compRes = [eventFromLocalDB.modifiedDate compare:dateOfEventFromServer];
                    
                    if (compRes==NSOrderedDescending) {
                        GSLog(GSLogWarning, @"[syncrhonizeEvents] Current event %@ has incorrect modifiedDate %@", eventFromLocalDB.topic, eventFromLocalDB.modifiedDate);
                        
                        eventFromLocalDB.modifiedDate = dateOfEventFromServer;
                        [self saveLocalDB];
                        
                    } else if (compRes==NSOrderedAscending) {
                        
                        GSLog(GSLogInfo, @"[syncrhonizeEvents] Current event %@ has to be updated from the server", eventFromLocalDB.topic);
                        
                        [eventFromLocalDB completeWithServerResponse:dict];
                        [self saveLocalDB];
                        
                        theListOfEventsIsChanged = YES;
                        
                    } else if (compRes==NSOrderedSame) {
                        GSLog(GSLogInfo, @"[syncrhonizeEvents] Current event %@ is up-to-date", eventFromLocalDB.topic);
                    }

                }
            }
        } //@synchronized(self)
        
        if (theListOfEventsIsChanged) {
            [self loadEventsFromLocalDB];
            [[NSNotificationCenter defaultCenter] postNotificationName:eventsDidUpdateNotification object:nil];
        } else {
            [[NSNotificationCenter defaultCenter] postNotificationName:eventsDidNotUpdateNotification object:nil];
        }
    }

    [self.updateEventsLock unlock];
    [self stopStatusSpinner];
}

-(void) synchronizeMyProfile {
    if (!_currentUser) {
        return;
    }
    
    [self startStatusSpinner];
    
    __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     getUserInfo: self.currentUser.tokenAgile
     onSuccess:^(NSDictionary * dictionaryOfUser) {
         
         GSLog(GSLogInfo, @"syncrhonizeMyProfile success: %@", dictionaryOfUser);
         
         __strong typeof(self)self = weakSelf;
         
         if (![dictionaryOfUser isKindOfClass:[NSNull class]]) {
             
                 RKDotNetDateFormatter * dtFormat = [RKDotNetDateFormatter dotNetDateFormatterWithTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
                 
                 //_currentUser.modifiedDate = [NSDate date];
                 
                 NSDate * dateOnServer = [dtFormat dateFromString:[dictionaryOfUser objectForKey:modifiedDateKey]];
                 
                 NSComparisonResult compRes = [_currentUser.modifiedDate compare:dateOnServer];

                 if (compRes==NSOrderedDescending) {
                     
                     GSLog(GSLogInfo, @"[syncrhonizeMyProfile] Current user has to be saved on the server");
                     
                     [self saveUserInfoToServer:_currentUser onComplete:nil];
                     [self sendPhotoToServerForUser:_currentUser onComplete:nil];
                     
                 } else if (compRes==NSOrderedAscending) {
                     
                     GSLog(GSLogInfo, @"[syncrhonizeMyProfile] Current user has to be loaded from the server");
                     
                     @synchronized(_currentUser) {

                         [_currentUser completeWithServerResponse:dictionaryOfUser];
                         _currentUser.isMyProfile = [NSNumber numberWithBool:YES];
                         _currentUser.isChanged = [NSNumber numberWithBool:NO];
                         
                     } //@synchronized(_currentUser)

                     [self saveLocalDB];
                     
                     [[NSNotificationCenter defaultCenter] postNotificationName:profileDidUpdateNotification object:nil];
                 } else if (compRes==NSOrderedSame) {
                     GSLog(GSLogInfo, @"[syncrhonizeMyProfile] Current user is up-to-date");
                 }
             
         } else {
             GSLog(GSLogWarning, @"[syncrhonizeMyProfile] Dictionary of user is null");
         }
         
         [self stopStatusSpinner];
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"[syncrhonizeMyProfile] getUserInfo failed. Error %@", error);
         
         __strong typeof(self)self = weakSelf;
         
         [self stopStatusSpinner];
     }];
}

-(void) synchronizeContactsStep1 {
    if (!_arrayOfContacts) {
        return;
    }
    
    /*if ([_arrayOfContacts count]==0) {
        return;
    }*/
    
    if (![self.updateContactsLock tryLock]) {
       return;
    }
    
    [self startStatusSpinner];
    
    [[NSNotificationCenter defaultCenter] postNotificationName:contactsWillUpdateNotification object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(synchronizeContactsStep2)
                                                 name:syncContactsListDidUpdateNotification
                                               object:nil];
    
    [self loadAllContactsListFromServer:YES];
}

-(void) synchronizeContactsStep2 {

    BOOL theContactsListIsChanged = NO;
    
    //[self deleteUsersFromLocalDBByCondition:@"isTemp=1"];
    
    if ([self.dictOfAllContacts count]==0) {
        
        GSLog(GSLogWarning, @"There are no contacts on the server. Synchronization skipped");
        [[NSNotificationCenter defaultCenter] postNotificationName:contactsDidNotUpdateNotification object:nil];
        
    } else {
    
        [[NSNotificationCenter defaultCenter] removeObserver:self name:syncContactsListDidUpdateNotification object:nil];
        
        RKDotNetDateFormatter * dtFormat = [RKDotNetDateFormatter dotNetDateFormatterWithTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
        
        for (GSUser * contact in _arrayOfContacts) {
            if ([contact.isdeleted boolValue]) {
                [self deleteContact:contact onComplete:nil];
                theContactsListIsChanged = YES;
            }
        }
        
        @synchronized(self) {
            for (GSUser * contact in _arrayOfContacts) {
                
                NSDictionary * dict = [self.dictOfAllContacts objectForKey:[NSNumber numberWithInt:[contact.sessionID intValue]]];
                
                if (!dict) {
                    [self deleteObjectInEntity:contact];
                    [self saveLocalDB];
                    theContactsListIsChanged = YES;
                } else {
                    if (([contact.isChanged boolValue]) && (![contact.isMyProfile boolValue])) { //Because we have already updated myProfile in "syncrhonizeMyProfile"
                        [self saveUserInfoToServer:contact onComplete:nil];
                    }
                }
            }
        }
        
        @synchronized(self) {
            for (NSDictionary * dict in [self.dictOfAllContacts allValues]) {
                
                NSNumber * contactIdFromServer = [dict objectForKey:userSessionIDKey];
                
                GSUser * contactFromLocalDB = [self contactBySessionID:contactIdFromServer];
                
                if (!contactFromLocalDB) {
                    GSUser * newContact = [self createContactInLocalDB];
                    
                    newContact.isMyProfile = [NSNumber numberWithBool:NO];
                    
                    [newContact completeWithServerResponse:dict];
                    newContact.isChanged = [NSNumber numberWithBool:NO];
                    newContact.isdeleted = [NSNumber numberWithBool:NO];
                    newContact.isTemp = [NSNumber numberWithBool:NO];
                    [self saveLocalDB];
                    
                    theContactsListIsChanged = YES;
                    
                } else {
                    
                    NSDate * dateOfContactFromServer = [dtFormat dateFromString:[dict objectForKey:modifiedDateKey]];
                    
                    NSComparisonResult compRes = [contactFromLocalDB.modifiedDate compare:dateOfContactFromServer];
                    
                    if (compRes==NSOrderedDescending) {
                        GSLog(GSLogWarning, @"[syncrhonizeContacts] Current contact %@ %@ has incorrect local modifiedDate %@",
                                                                                          contactFromLocalDB.firstName,
                                                                                          contactFromLocalDB.lastName,
                                                                                          contactFromLocalDB.modifiedDate);
                        contactFromLocalDB.modifiedDate = dateOfContactFromServer;
                        [self saveLocalDB];
                        
                    } else if (compRes==NSOrderedAscending) {
                        
                        GSLog(GSLogInfo, @"[syncrhonizeContacts] Current contact %@ %@ has to be loaded from the server", contactFromLocalDB.firstName, contactFromLocalDB.lastName);
                        
                        contactFromLocalDB.photo = nil;
                        contactFromLocalDB.imageWasLoaded = NO;
                        [self requestImageForContactByContact:contactFromLocalDB onComplete:^(BOOL result, UIImage *image) {
                            
                        }];
                        
                        [contactFromLocalDB completeWithServerResponse:dict];
                        contactFromLocalDB.isChanged = [NSNumber numberWithBool:NO];
                        [self saveLocalDB];
                        
                        theContactsListIsChanged = YES;
                        
                    } else if (compRes==NSOrderedSame) {
                        GSLog(GSLogInfo, @"[syncrhonizeEvents] Current contact %@ %@ is up-to-date", contactFromLocalDB.firstName, contactFromLocalDB.lastName);
                    }
                    
                }
            }
        } //@syncgronized(self)
        
        /*if (theContactsListIsChanged) {
            [self loadContactsFromLocalDB];
            [[NSNotificationCenter defaultCenter] postNotificationName:contactsDidUpdateNotification object:nil];
        } else {
            [[NSNotificationCenter defaultCenter] postNotificationName:contactsDidNotUpdateNotification object:nil];
        }*/
        
        [self loadContactsFromLocalDB];
        [[NSNotificationCenter defaultCenter] postNotificationName:contactsDidUpdateNotification object:nil];
    }
    
    [self.updateContactsLock unlock];
    [self stopStatusSpinner];
}


#pragma mark - Getters And Setters

-(GSUser*) currentUser {
    if (!_currentUser) {
        if (![self loadMyUserFromLocalDB]) {
            [self createMyUserInLocalDB];
        }
    }
    
    return _currentUser;
}

-(NSArray*) arrayOfEvents {
    if (!_arrayOfEvents) {
        if (![self loadEventsFromLocalDB]) {
            [self loadEventsFromServer:YES];
            [self loadEventsFromLocalDB];
        }
    } else {
        if ([_arrayOfEvents count]==0) {
            if (![self loadEventsFromLocalDB]) {
                [self loadEventsFromServer:YES];
                [self loadEventsFromLocalDB];
            }
        }
    }

    return _arrayOfEvents;
}

-(NSArray*) arrayOfContacts {
    if (!_arrayOfContacts) {
        if (![self loadContactsFromLocalDB]) {
            [self loadContactsFromServer:YES];
            [self loadContactsFromLocalDB];
        }
    } else {
        if ([_arrayOfContacts count]==0) {
            if (![self loadContactsFromLocalDB]) {
                [self loadContactsFromServer:YES];
                [self loadContactsFromLocalDB];
            }
        }
    }
    
    return _arrayOfContacts;
}


#pragma mark - Queries to Server

-(void) loadMyProfileInfoFromServer {

    __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     getUserInfo: self.currentUser.tokenAgile
     onSuccess:^(NSDictionary * dictionaryOfUser) {
         
         GSLog(GSLogInfo, @"getUserInfo success: %@", dictionaryOfUser);
         
         __strong typeof(self)self = weakSelf;
         
         if (![dictionaryOfUser isKindOfClass:[NSNull class]]) {

             @synchronized(self.currentUser) {
                 [self.currentUser completeWithServerResponse:dictionaryOfUser];
                 self.currentUser.isSynchronized = [NSNumber numberWithBool:YES];
                 self.currentUser.isRegistered = [NSNumber numberWithBool:YES];
                 self.currentUser.isChanged = [NSNumber numberWithBool:NO];
                 self.currentUser.isMyProfile = [NSNumber numberWithBool:YES];
                 self.currentUser.isTemp = [NSNumber numberWithBool:NO];
             }
             
             [self saveLocalDB];
             
         } else {
             
             GSLog(GSLogWarning, @"Dictionary of user is null");
             
         }
         
         [[NSNotificationCenter defaultCenter] postNotificationName:profileDidUpdateNotification object:nil];
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"getUserInfo failed. Error %@", error);
         
         [[NSNotificationCenter defaultCenter] postNotificationName:profileDidNotUpdateNotification object:nil];
     }];
}

-(void) saveUserInfoToServer:(GSUser*) user
                  onComplete:(void(^)(BOOL result)) completitionBlock {
    
    __weak __typeof(self)weakSelf = self;
    
    if ([user.isMyProfile boolValue]) {
        
        NSDictionary * dictionaryOfUser = nil;
        
        @autoreleasepool {
            @synchronized(_currentUser) {
                dictionaryOfUser = [_currentUser returnDictionaryForAgileServer];
            }
        }
        
        [[SGSServerManager sharedManager]
         postUserInfo: self.currentUser.tokenAgile
         dictOfUser:dictionaryOfUser
         onSuccess:^() {
             GSLog(GSLogInfo, @"[saveUserInfoToServer] postUserInfo success");
             
             __strong typeof(self)self = weakSelf;
             
             @synchronized(self.currentUser) {
                 self.currentUser.isChanged = [NSNumber numberWithBool:NO];
             }
             
             [self saveLocalDB];
             
             [self updateMyProfileDateFromServer];
             
             if (completitionBlock) {
                 completitionBlock(YES);
             }
             
         } onFailure:^(NSError *error, NSInteger statusCode) {
             GSLog(GSLogError, @"[saveUserInfoToServer] postUserInfo failed. Error %@", error);
             
             if (completitionBlock) {
                 completitionBlock(NO);
             }
         }];
        
    } else {
        
        [[SGSServerManager sharedManager]
         saveNotesForContact: self.currentUser.tokenAgile
         toUserID: [NSNumber numberWithInteger:[user.sessionID integerValue]]
         notes: user.notes
         onSuccess:^() {
             
             GSLog(GSLogInfo, @"[saveUserInfoToServer] saveNotesForContact success");
             
             __strong typeof(self)self = weakSelf;
             
             @synchronized(self) {
                 user.isChanged = [NSNumber numberWithBool:NO];
             }
             
             [self saveLocalDB];
             
             if (completitionBlock) {
                 completitionBlock(YES);
             }
             
         } onFailure:^(NSError *error, NSInteger statusCode) {
             GSLog(GSLogError, @"[saveUserInfoToServer] saveNotesForContact failed. Error: %@", error.localizedDescription);
             
             if (completitionBlock) {
                 completitionBlock(NO);
             }
         }];
    }
}

-(void) saveUserChangesAndSendToServer:(GSUser*) user
                           newUserData:(NSDictionary*) userDict
                            onComplete:(void(^)(BOOL result)) completitionBlock {

    @synchronized(_currentUser) {
        [_currentUser completeWithServerResponse:userDict];
        _currentUser.isChanged = [NSNumber numberWithBool:YES];
        _currentUser.isdeleted = [NSNumber numberWithBool:NO];
        _currentUser.modifiedDate = [NSDate date];
    }
    
    [self saveLocalDB];
    
    __weak __typeof(self) weakSelf = self;
    
    [self saveUserInfoToServer:_currentUser onComplete:^(BOOL result) {
        
        __strong typeof(self)self = weakSelf;
        
        _currentUser.isChanged = [NSNumber numberWithBool:NO];

        [self saveLocalDB];
        
        if (completitionBlock) {
            completitionBlock(result);
        }
    }];
    
    [[NSNotificationCenter defaultCenter] postNotificationName:profileDidUpdateNotification object:nil];
}


-(void) sendPhotoToServerForUser:(GSUser*) user
                      onComplete:(void(^)(BOOL result)) completitionBlock {
    
    __weak __typeof(self) weakSelf = self;
    
    if (user.photo) {
        [[SGSServerManager sharedManager]
         postUserImage: self.currentUser.tokenAgile
         imageOfUser: user.photo
         onSuccess:^(NSString * newURL) {
             
             GSLog(GSLogInfo, @"[sendPhotoToServerForUser] postUserImage success");
             
             __strong typeof(self)self = weakSelf;
             
             @synchronized (self.currentUser) {
                 user.isChanged = [NSNumber numberWithBool:NO];
                 user.imageURL = newURL;
             }
             
             [self saveLocalDB];
             
             if (completitionBlock) {
                 completitionBlock(YES);
             }
             
             //[self updateMyProfileImageURLFromServer];
             [self updateMyProfileDateFromServer];
             
         } onFailure:^(NSError *error, NSInteger statusCode) {
             GSLog(GSLogError, @"[sendPhotoToServerForUser] postUserImage failed. Error %@", error);
             
             if (completitionBlock) {
                 completitionBlock(NO);
             }

         }];
    } else {
        [[SGSServerManager sharedManager]
         deleteUserImage: self.currentUser.tokenAgile
         onSuccess:^() {
             
             GSLog(GSLogInfo, @"[sendPhotoToServerForUser] deleteUserImage success");
             
             __strong typeof(self)self = weakSelf;
             
             @synchronized(self.currentUser) {
                 user.isChanged = [NSNumber numberWithBool:NO];
                 user.imageURL = nil;
             }
             
             [self saveLocalDB];
             
             [self updateMyProfileDateFromServer];
             
             if (completitionBlock) {
                 completitionBlock(YES);
             }
             
             
         } onFailure:^(NSError *error, NSInteger statusCode) {
             GSLog(GSLogError, @"[sendPhotoToServerForUser] deleteUserImage failed. Error %@", error);
             
             if (completitionBlock) {
                 completitionBlock(NO);
             }
             
         }];
    }
}

-(void) updateMyProfileDateFromServer {
    
    __weak __typeof(self) weakSelf = self;
    
    [[SGSServerManager sharedManager]
     getUserInfo: self.currentUser.tokenAgile
     onSuccess:^(NSDictionary * dictionaryOfUser) {
         
         //GSLog(GSLogInfo, @"[updateMyProfileDateFromServer] getUserInfo success: %@", dictionaryOfUser);
         
         __strong typeof(self)self = weakSelf;
         
         if (![dictionaryOfUser isKindOfClass:[NSNull class]]) {
             
             RKDotNetDateFormatter * dtFormat = [RKDotNetDateFormatter dotNetDateFormatterWithTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
             
             @synchronized(self.currentUser) {
                 self.currentUser.modifiedDate   = [dtFormat dateFromString:[dictionaryOfUser objectForKey:modifiedDateKey]];
             }
             
             [self saveLocalDB];
             
         } else {
             GSLog(GSLogWarning, @"[updateMyProfileDateFromServer] Dictionary of user is null");
         }
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"[updateMyProfileDateFromServer] getUserInfo failed. Error %@", error);
     }];
}

-(void) updateMyProfileImageURLFromServer {
    
    __weak __typeof(self) weakSelf = self;
    
    [[SGSServerManager sharedManager]
     getUserInfo: self.currentUser.tokenAgile
     onSuccess:^(NSDictionary * dictionaryOfUser) {
         
         GSLog(GSLogInfo, @"[updateMyProfileImageURLFromServer] getUserInfo success: %@", dictionaryOfUser);
         
         __strong typeof(self)self = weakSelf;
         
         if (![dictionaryOfUser isKindOfClass:[NSNull class]]) {
             
             @synchronized(self.currentUser) {
                 self.currentUser.imageURL  = [dictionaryOfUser objectForKey:userPhotoKey];
             }
             
             [self saveLocalDB];
             
         } else {
             GSLog(GSLogWarning, @"[updateMyProfileImageURLFromServer] Dictionary of user is null");
         }
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"[updateMyProfileImageURLFromServer] getUserInfo failed. Error %@", error);
     }];
}


// Дозагрузка event'ов
-(void) loadEventsFromServer:(BOOL) cleanDBBeforeLoad {
    
    if (![self.updateEventsLock tryLock]) return;
    
    [[NSNotificationCenter defaultCenter] postNotificationName:eventsWillRenewNotification object:nil];
    
    if (cleanDBBeforeLoad) {
        [self deleteAllEventsFromLocalDB];
    }
    
    __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     getEventsList: self.currentUser.tokenAgile
     //offcet:self.eventsCount // offset
     offcet:[_arrayOfEvents count]+1
     count:eventsInRequest                                      // This is a const in the top of this file
     onSuccess:^(NSArray * arrayWithDictionaryOfEvents) {
         
         __strong typeof(self)self = weakSelf;
         
         if (![arrayWithDictionaryOfEvents isKindOfClass:[NSNull class]]) {
             
             GSLog(GSLogInfo, @"[loadEventsFromServer] GetEventsList count: %d", [arrayWithDictionaryOfEvents count]);

             if ([arrayWithDictionaryOfEvents count]>0) {
                 
                 @synchronized(self) {
                     for (NSDictionary * dict in arrayWithDictionaryOfEvents) {

                         if (![self eventByID:[dict objectForKey:eventIDKey]]) {

                             GSEvent * newEvent = [self createEventInLocalDB];
                             
                             [newEvent completeWithServerResponse:dict];
                         }
                     }
                 } //@synchronized(self)
                 
                 [self saveLocalDB];
                 
                 [[NSNotificationCenter defaultCenter] postNotificationName:eventsDidRenewNotification object:nil];
             }
         } else {
             GSLog(GSLogWarning, @"Array of events is null");
             [[NSNotificationCenter defaultCenter] postNotificationName:eventsDidNotRenewNotification object:nil];
         }
         
         [self.updateEventsLock unlock];
     } onFailure:^(NSError *error, NSInteger statusCode) {
         
         GSLog(GSLogError, @"GetEventsResult failed. Error: %@", error);
         
         [[NSNotificationCenter defaultCenter] postNotificationName:eventsDidNotRenewNotification object:nil];
         
         [self.updateEventsLock unlock];
     }];
    
}

-(void) loadAllFutureEventsListFromServer:(BOOL) autoClear {
    
    if (autoClear) {
        [self.dictOfAllFutureEvents removeAllObjects];
    }
    
    __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     getEventsList: self.currentUser.tokenAgile
     offcet:[self.dictOfAllFutureEvents count]+1
     count:eventsInRequest
     onSuccess:^(NSArray * arrayWithDictionaryOfEvents) {
         
         __strong typeof(self)self = weakSelf;
         
         if (![arrayWithDictionaryOfEvents isKindOfClass:[NSNull class]]) {
             
             GSLog(GSLogInfo, @"[loadAllFutureEventsListFromServer] GetEventsList count: %d", [arrayWithDictionaryOfEvents count]);
             
             if ([arrayWithDictionaryOfEvents count] > 0) {
                 for (NSDictionary * dict in arrayWithDictionaryOfEvents) {

                     NSNumber * eventId = [dict objectForKey:eventIDKey];
                     
                     [weakSelf.dictOfAllFutureEvents setObject:dict forKey:eventId];
                 }
                 
                 [self loadAllFutureEventsListFromServer:NO]; // Рекурсия
             } else {
                 
                 GSLog(GSLogWarning, @"[loadAllFutureEventsListFromServer] Array of events is empty");
                 
                 self.isFutureEventsDictUpdated = YES;
                 [self sendNotification:syncFutureEventsListDidUpdateNotification];
             }
             
         } else {
             GSLog(GSLogWarning, @"[loadAllFutureEventsListFromServer] Array of events is null");
             
              self.isFutureEventsDictUpdated = YES;
              [self sendNotification:syncFutureEventsListDidUpdateNotification];
         }
     } onFailure:^(NSError *error, NSInteger statusCode) {
         
         GSLog(GSLogError, @"[loadAllFutureEventsListFromServer] GetEventsResult failed. Error: %@", error);
         
         __strong typeof(self)self = weakSelf;
         
          self.isFutureEventsDictUpdated = NO;
         [self sendNotification:syncFutureEventsListDidUpdateNotification];
         
     }];
}

-(void) loadAllBackEventsListFromServer:(BOOL) autoClear {
    
    if (autoClear) {
        [self.dictOfAllBackEvents removeAllObjects];
    }
    
    NSInteger offcetForRequest = ([self.dictOfAllBackEvents count] + 1) * -1;
    
    __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     getEventsList: self.currentUser.tokenAgile
     offcet:offcetForRequest
     count:eventsInRequest
     onSuccess:^(NSArray * arrayWithDictionaryOfEvents) {
         
         __strong typeof(self)self = weakSelf;
         
         if (![arrayWithDictionaryOfEvents isKindOfClass:[NSNull class]]) {
             
             GSLog(GSLogInfo, @"[loadAllBackEventsListFromServer] GetEventsList count: %d", [arrayWithDictionaryOfEvents count]);
             
             if ([arrayWithDictionaryOfEvents count]>0) {
                 for (NSDictionary * dict in arrayWithDictionaryOfEvents) {
                 
                     NSNumber * eventId = [dict objectForKey:eventIDKey];
                    [self.dictOfAllBackEvents setObject:dict forKey:eventId];
                
                 }
             
                 [self loadAllBackEventsListFromServer:NO]; //Рекурсия
             } else {
                GSLog(GSLogWarning, @"[loadAllBackEventsListFromServer] Array of events is empty");
                 
                self.isBackEventsDictUpdated = YES;
                [self sendNotification:syncBackEventsListDidUpdateNotification];
             }
             
         } else {
             GSLog(GSLogWarning, @"[loadAllBackEventsListFromServer] Array of events is null");
             self.isBackEventsDictUpdated = YES;
             [self sendNotification:syncBackEventsListDidUpdateNotification];
         }
     } onFailure:^(NSError *error, NSInteger statusCode) {
         
         GSLog(GSLogError, @"[loadAllBackEventsListFromServer] GetEventsResult failed. Error: %@", error);
         
         __strong typeof(self)self = weakSelf;
         
         self.isBackEventsDictUpdated = NO;
         [self sendNotification:syncBackEventsListDidUpdateNotification];
         
     }];
}

-(void) loadAllContactsListFromServer:(BOOL) autoClear {
    
    if (autoClear) {
        [self.dictOfAllContacts removeAllObjects];
    }
    
    __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     getContactsList: self.currentUser.tokenAgile
     offcet:[self.dictOfAllContacts count]+1
     count:contactsInRequest
     onSuccess:^(NSArray * arrayWithDictionaryOfContacts) {
         
         __strong typeof(self)self = weakSelf;
         
         if (![arrayWithDictionaryOfContacts isKindOfClass:[NSNull class]]) {
             
             GSLog(GSLogInfo, @"[loadAllContactsListFromServer] getContactsList count: %d", [arrayWithDictionaryOfContacts count]);
             
             if ([arrayWithDictionaryOfContacts count]>0) {
                 for (NSDictionary * dict in arrayWithDictionaryOfContacts) {
                     
                     NSNumber * contactId =  [dict objectForKey:userSessionIDKey];

                     [self.dictOfAllContacts setObject:dict forKey:contactId];
                 }
                 
                 [self loadAllContactsListFromServer:NO]; //Рекурсия
             } else {
               GSLog(GSLogWarning, @"[loadAllContactsListFromServer] Array of contacts is empty");
               [self sendNotification:syncContactsListDidUpdateNotification];
             }
             
         } else {
             GSLog(GSLogWarning, @"[loadAllContactsListFromServer] Array of contacts is null");
             [self sendNotification:syncContactsListDidUpdateNotification];
         }
     } onFailure:^(NSError *error, NSInteger statusCode) {
         
         GSLog(GSLogError, @"[loadAllContactsListFromServer] getContactsList failed. Error: %@", error);
         [self sendNotification:syncContactsListDidUpdateNotification];
         
     }];
}


-(void) loadContactsFromServer:(BOOL) cleanDBBeforeLoad {
    
    if (![self.updateContactsLock tryLock]) return;
    
    [[NSNotificationCenter defaultCenter] postNotificationName:contactsWillRenewNotification object:nil];
    
    if (cleanDBBeforeLoad) {
        [self deleteUsersFromLocalDBByCondition:@"isMyProfile=0"];
    }
    
    __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     getContactsList: self.currentUser.tokenAgile
     offcet:[_arrayOfContacts count] + 1
     count:contactsInRequest
     onSuccess:^(NSArray * arrayWithDictionaryOfContacts) {
         
         __strong typeof(self)self = weakSelf;
         
         if (![arrayWithDictionaryOfContacts isKindOfClass:[NSNull class]]) {
             
             GSLog(GSLogInfo, @"[loadContactsFromServer] getContactsList count: %d", [arrayWithDictionaryOfContacts count]);
             
             if ([arrayWithDictionaryOfContacts count]>0) {
                 @synchronized(self) {
                     for (NSDictionary * dict in arrayWithDictionaryOfContacts) {
                         
                         if (![self contactBySessionID:[dict objectForKey:userIDKey]]) {  //Search contact by Session Id, if not found then create it
                         
                             GSUser * newContact = [self createContactInLocalDB];
                             
                             [newContact completeWithServerResponse:dict];
                             newContact.isMyProfile = [NSNumber numberWithBool:NO];
                             newContact.isChanged = [NSNumber numberWithBool:NO];
                             newContact.isdeleted = [NSNumber numberWithBool:NO];
                         }
                    
                     }
 
                     [self saveLocalDB];
                     [self loadContactsFromLocalDB];
                 } //@synchronized(self)
                 
                 [[NSNotificationCenter defaultCenter] postNotificationName:contactsDidRenewNotification object:nil];
             } else {
                 GSLog(GSLogWarning, @"[loadContactsFromServer] Array of contacts is empty");
                 [[NSNotificationCenter defaultCenter] postNotificationName:contactsDidNotRenewNotification object:nil];
             }
             
         } else {
             GSLog(GSLogWarning, @"[loadContactsFromServer] Array of contacts is null");
             [[NSNotificationCenter defaultCenter] postNotificationName:contactsDidNotRenewNotification object:nil];
         }

         [self.updateContactsLock unlock];
     } onFailure:^(NSError *error, NSInteger statusCode) {
         
         GSLog(GSLogError, @"[loadContactsFromServer] Failed! Error: %@", error);
         
         [[NSNotificationCenter defaultCenter] postNotificationName:contactsDidNotRenewNotification object:nil];
         
         [self.updateContactsLock unlock];
     }];
}

#pragma mark - Local Database

-(BOOL) saveLocalDB {

    NSManagedObjectContext * currentContext;

#if CORE_DATA_SAFE_THREAD_MODE
    currentContext = [self managedObjectContextForCurrentThread];
#else
    currentContext = [self defaultManagedObjectContext];
#endif

    return [self saveLocalDBInContext:currentContext];
}

//-----------------------------------------------------------------------------------------------//

-(BOOL) saveLocalDBInContext:(NSManagedObjectContext*) context {
    
    NSError * error;
    __block BOOL result = YES;

    if (![context save:&error]) {
        GSLog(GSLogError, @"[saveLocalDBInContext] Save to database error (child context): %@", [error localizedDescription]);
        result = NO;
    } else {
        if (![context isEqual:self.defaultManagedObjectContext]) {
            [self.defaultManagedObjectContext performBlockAndWait:^{
                NSError *e = nil;
                if (![self.defaultManagedObjectContext save:&e])
                {
                    GSLog(GSLogError, @"[saveLocalDBInContext] Save to database error (parent context): %@", [error localizedDescription]);
                    result = NO;
                } else {
                    GSLog(GSLogInfo, @"[saveLocalDBInContext] success");
                    result = YES;
                }
                
            }];
        }
    }
    
    return result;
}

//-----------------------------------------------------------------------------------------------//

-(NSArray*) executeRequestToLocalDB:(NSString*) entityName
                         condition:(NSString*) condition
                      orderByArray:(NSArray*) orderByArray {
    
    NSManagedObjectContext * currentContext;
    NSArray * result = nil;
    NSError * error = nil;
    NSFetchRequest * request = [[NSFetchRequest alloc] init];
    NSEntityDescription * entity;

#if CORE_DATA_SAFE_THREAD_MODE
    currentContext = [self managedObjectContextForCurrentThread];
#else
    currentContext = [self defaultManagedObjectContext];
#endif
    
    entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:currentContext];
    
    if (condition) {
        NSPredicate * predicate = [NSPredicate predicateWithFormat:condition];
        [request setPredicate:predicate];
    }
    
    if (orderByArray) {
        
        NSMutableArray * sortDescriptors = [[NSMutableArray alloc] init];
        
        for (NSString * str in orderByArray) {
            NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:str ascending:YES];
            [sortDescriptors addObject:sortDescriptor];
        }
        [request setSortDescriptors:sortDescriptors];
    }
    
    [request setEntity:entity];
    [request setResultType:NSManagedObjectResultType];
    
    result = [currentContext executeFetchRequest:request error:&error];
    
    if (error) {
        GSLog(GSLogError, @"executeRequestToLocalDB Fetch request error: %@", [error localizedDescription]);
        return nil;
    } else {
        GSLog(GSLogInfo, @"[executeRequestToLocalDB] success. Entity: %@ Condition: %@ Array count: %d", entityName, condition, [result count]);
        return result;
    }
}


//-----------------------------------------------------------------------------------------------//


-(BOOL) deleteRecordsInEntity:(NSString*) entityName
                    condition:(NSString*) condition
{
    NSManagedObjectContext * currentContext;
 
#if CORE_DATA_SAFE_THREAD_MODE
    currentContext = [self managedObjectContextForCurrentThread];
#else
    currentContext = [self defaultManagedObjectContext];
#endif

    NSArray * resultSet = [self executeRequestToLocalDB:entityName condition:condition orderByArray:nil];
    
    for (NSManagedObject * record in resultSet) {
        [currentContext deleteObject:record];
        GSLog(GSLogWarning, @"Record deleted from Entity [%@] due to condition \"%@\"", entityName, condition);
    }
    
    return [self saveLocalDBInContext:currentContext];
}

//-----------------------------------------------------------------------------------------------//


-(id) insertObjectToEntity:(NSString*) entityName {

    NSManagedObjectContext * currentContext;
    
#if CORE_DATA_SAFE_THREAD_MODE
    currentContext = [self managedObjectContextForCurrentThread];
#else
    currentContext = [self defaultManagedObjectContext];
#endif
    
    id result = [NSEntityDescription insertNewObjectForEntityForName:entityName
                                         inManagedObjectContext:currentContext];
    
    [self saveLocalDBInContext:currentContext];
    
    GSLog(GSLogWarning, @"Record inserted to Entity [%@]", entityName);

    return result;

}

//-----------------------------------------------------------------------------------------------//

-(BOOL) deleteObjectInEntity:(NSManagedObject*) object { //Delete one record from DB

    NSManagedObjectContext * currentContext;
    
#if CORE_DATA_SAFE_THREAD_MODE
    currentContext = [self managedObjectContextForCurrentThread];
#else
    currentContext = [self defaultManagedObjectContext];
#endif

    [currentContext deleteObject:object];
    
    GSLog(GSLogWarning, @"Record %@ deleted from Entity [%@]", [object description], [[object entity] name]);
    
    return [self saveLocalDBInContext:currentContext];
}

//-----------------------------------------------------------------------------------------------//

-(BOOL) createMyUserInLocalDB {
    
    _currentUser = [self insertObjectToEntity:@"GSUser"];

    if (_currentUser)
    @synchronized (_currentUser) {
        
        _currentUser.isMyProfile = [NSNumber numberWithBool:YES];
        _currentUser.modifiedDate = [NSDate dateWithTimeIntervalSince1970:0];
        _currentUser.isChanged = [NSNumber numberWithBool:YES];
        _currentUser.isSynchronized = [NSNumber numberWithBool:NO];
        _currentUser.isdeleted = [NSNumber numberWithBool:NO];
        _currentUser.isTemp = [NSNumber numberWithBool:NO];
        
    }
    
    return [self saveLocalDB];
}

-(GSUser*) createContactInLocalDB {
    
    GSLog(GSLogInfo, @"createContactInLocalDB executed");
    
    return (GSUser*) [self insertObjectToEntity:@"GSUser"];
}

-(GSEvent*) createEventInLocalDB {
    
    GSLog(GSLogInfo, @"createEventInLocalDB executed");
    
    return (GSEvent*) [self insertObjectToEntity:@"GSEvent"];
    
}

-(BOOL) loadMyUserFromLocalDB {
    NSArray * userArray = [self executeRequestToLocalDB:@"GSUser" condition:@"isMyProfile = 1" orderByArray:nil];
    
    if ((userArray) && [userArray count]>0) {
        _currentUser = [userArray objectAtIndex:0];
        return YES;
    } else {
        return NO;
    }
    
}

-(BOOL) loadEventsFromLocalDB {
    
    @synchronized(self) {
        _arrayOfEvents = [self executeRequestToLocalDB:@"GSEvent" condition:nil orderByArray:@[@"startTime"]];
        
        if ((_arrayOfEvents) && [_arrayOfEvents count]>0) {
            return YES;
        } else {
            return NO;
        }
    }
}

-(BOOL) deleteAllContactsFromLocalDB {
    return [self deleteRecordsInEntity:@"GSUser" condition:@"isMyProfile = 0"];
}

-(BOOL) deleteMyProfileFromLocalDB {
    return [self deleteRecordsInEntity:@"GSUser" condition:@"isMyProfile = 1"];
}

-(BOOL) deleteAllUsersFromLocalDB {
    return [self deleteRecordsInEntity:@"GSUser" condition:nil];
}

-(BOOL) deleteUsersFromLocalDBByCondition:(NSString*) condition {
    return [self deleteRecordsInEntity:@"GSUser" condition:condition];
}


-(BOOL) deleteAllEventsFromLocalDB {
    return [self deleteRecordsInEntity:@"GSEvent" condition:nil];
}

-(BOOL) deleteEventsFromLocalDBByCondition:(NSString*) condition {
    return [self deleteRecordsInEntity:@"GSEvent" condition:condition];
}

-(BOOL) loadContactsFromLocalDB {
    _arrayOfContacts = [self executeRequestToLocalDB:@"GSUser" condition:@"isMyProfile = 0" orderByArray:@[@"firstName",@"lastName"]];
    
    if ((_arrayOfContacts) && [_arrayOfContacts count]>0) {
        return YES;
    } else {
        return NO;
    }

}

#pragma mark - Functions for Images

-(void) getImageByURL:(NSString*) url
     placeholderImage:(UIImage*) placeholderImage
           onComplete:(void(^)(BOOL result, UIImage * image)) completitionBlock {

#if APPLE_STANDARD_IMAGE_DOWNLOADER
    dispatch_queue_t backgroundQueue =
                    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(backgroundQueue, ^{
        NSData *avatarData = nil;
        NSString *imageURLString = url;
        if (imageURLString) {
            NSURL *imageURL = [NSURL URLWithString:imageURLString];
            avatarData = [NSData dataWithContentsOfURL:imageURL];
        }
        
        bool requestResult = NO;
        UIImage * resultImage = placeholderImage;
        
        if ((avatarData) && ([avatarData bytes]>0)) {
            requestResult = YES;
            resultImage = [UIImage imageWithData:avatarData];
        }
        
        // Update UI from the main thread when available
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completitionBlock) {
                completitionBlock(requestResult, resultImage);
            }
        });
        
    });
#else //Use UIImageView+AFNetworking.m
    NSURL * urlWithPhoto = [NSURL URLWithString:url];
    NSURLRequest * urlRequest = [NSURLRequest requestWithURL:urlWithPhoto];
    
    self.photoImageView = [[UIImageView alloc] init];
    
    [self.photoImageView
     setImageWithURLRequest:urlRequest
     placeholderImage:placeholderImage
     success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
         
         GSLog(GSLogInfo, @"[getImageByURL] setImageWithURLRequest success");
         
         if (completitionBlock) {
             completitionBlock(YES,image);
         }
         
     }
     failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
         GSLog(GSLogError, @"[requestImageForEventByIndex] setImageWithURLRequest failed. Error: %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO,placeholderImage);
         }
     }];
#endif
}

-(void) requestImageForEventByEvent:(GSEvent *) event
                    //loadToImageView:(UIImageView*) imageView
                         onComplete:(void(^)(BOOL result, UIImage * image)) completitionBlock
{
    __weak __typeof(self)weakSelf = self;
    
    //GSLog(GSLogWarning, @"[requestImageForEventByEvent] Current Event: %@", [event topic]);
    
    if (event.eventPhoto) {
        /*if (imageView) {
            imageView.image = [UIImage imageWithData:event.eventPhoto];
            imageView.hidden = NO;
        }*/
        if (completitionBlock) {
            completitionBlock(YES,[UIImage imageWithData:event.eventPhoto]);
        }

    } else {
        GSLog(GSLogWarning, @"Load foto for event: %@", [event topic]);
        
        if ([event.eventImageURL length]>0) {
            
            [self getImageByURL:event.eventImageURL
               placeholderImage:[UIImage imageNamed:No_Event_Photo_Name]
                     onComplete:^(BOOL result, UIImage *image) {
                         
                         __strong typeof(self)self = weakSelf;
                         
                         if (result) {
                             event.eventPhoto = UIImagePNGRepresentation(image);
                             [self saveLocalDB];
                         }
                         /*if (imageView) {
                             imageView.image = image;
                             imageView.hidden = NO;
                         }*/
                         
                         if (completitionBlock) {
                             completitionBlock(result,image);
                         }
                     }
             ];
        } else {
            event.eventPhoto = [NSData data];

            /*if (imageView) {
                imageView.image = [[UIImage alloc] init];
                imageView.hidden = NO;
            }*/
            if (completitionBlock) {
                completitionBlock(NO,[[UIImage alloc] init]);
            }

        }
    }
}

-(void) updateUserImage:(NSData*) userImage
             forUser:(GSUser*) user
{
    if ((!user.photo) & (!userImage)) {
        return; // Both are nil
    }
//  Здесь было падение приложения, по всей вероятности user==nil
    @synchronized (_arrayOfContacts) {
        if ((userImage) && (user)) {
            user.photo = userImage;
        } else if (user) {
            user.photo = nil;
        }
    }
    
    [self saveLocalDB];
    
    if (user) {
        if ([user.isMyProfile boolValue]) {
            [[NSNotificationCenter defaultCenter] postNotificationName:profileImageDidUpdateNotification object:nil];
        } else {
            NSDictionary * dict = [NSDictionary dictionaryWithObject:user.sessionID forKey:contactImageUpdateContactIdKey];
            [[NSNotificationCenter defaultCenter] postNotificationName:contactImageDidUpdateNotification object:dict];
        }
    }
}

-(void) saveUserImageAndSendToServer:(UIImage*) userImage
                             forUser:(GSUser *) user {
    
    [self updateUserImage:UIImagePNGRepresentation(userImage) forUser:user];
    
    @synchronized (_arrayOfContacts) {
        user.isChanged = [NSNumber numberWithBool:YES];
        user.modifiedDate = [NSDate date];
    }
    
    __weak __typeof(self)weakSelf = self;
    
    [self sendPhotoToServerForUser:user onComplete:^(BOOL result) {
        
        __strong typeof(self)self = weakSelf;
        
        @synchronized (self.arrayOfContacts) {
            user.isChanged = [NSNumber numberWithBool:NO];
        }
    }];
}

-(void) deleteUserImageAndSendToServer:(GSUser *) user {

    [self updateUserImage:nil forUser:user];
    
    @synchronized (_arrayOfContacts) {
        user.isChanged = [NSNumber numberWithBool:YES];
        user.modifiedDate = [NSDate date];
    }
    
    __weak __typeof(self)weakSelf = self;
    
    [self sendPhotoToServerForUser:user onComplete:^(BOOL result) {
        
        __strong typeof(self)self = weakSelf;
        
        @synchronized (self.arrayOfContacts) {
            user.isChanged = [NSNumber numberWithBool:NO];
        }
    }];
}

-(void) requestImageForEventByIndex:(NSInteger) eventIndex
          //loadToImageView:(UIImageView*) imageView
                         onComplete:(void(^)(BOOL result, UIImage * image)) completitionBlock
{
    GSEvent * event = [_arrayOfEvents objectAtIndex:eventIndex];
    
    //[self requestImageForEventByEvent:event loadToImageView:imageView];
    [self requestImageForEventByEvent:event onComplete:completitionBlock];
}

-(void) requestImageForContactByContact:(GSUser *) contact
                        //loadToImageView:(UIImageView*) imageView
                             onComplete:(void(^)(BOOL result, UIImage * image)) completitionBlock
{
    if (!contact) return;
    
    __weak __typeof(self)weakSelf = self;
    
    UIImage * noImage = [UIImage imageNamed:[contact.isMyProfile boolValue] ? Add_Profile_Photo_Name : No_Profile_Photo_Name];

    @synchronized (_arrayOfContacts) {
        contact.emptyImage = [UIImage imageNamed:[contact.isMyProfile boolValue] ? Add_Profile_Photo_Name : No_Profile_Photo_Name];
    }
    
    if (contact.photo) {
        if (completitionBlock) {
            completitionBlock(YES,[UIImage imageWithData:contact.photo]);
        }
    } else {
        
        GSLog(GSLogWarning, @"Load foto for contact: %@", [contact fullName]);
        
        if (([contact.imageURL length]>0) && (!contact.imageWasLoaded)){
            contact.imageWasLoaded = YES;
            [self getImageByURL:contact.imageURL
               placeholderImage:noImage
                     onComplete:^(BOOL result, UIImage *image) {
                         
                         __strong typeof(self)self = weakSelf;
                         
                         if (result) {
                             [self updateUserImage:UIImagePNGRepresentation(image) forUser:contact];
                             [self saveLocalDB];
                         } else {
                             
                         }

                         if (completitionBlock) {
                             completitionBlock(result,image);
                         }
                     }
             ];
        } else {
            completitionBlock(NO,noImage);
        }
    }
}

-(void) requestImageForContactByIndex:(NSInteger) contactIndex
                      //loadToImageView:(UIImageView*) imageView
                           onComplete:(void(^)(BOOL result, UIImage * image)) completitionBlock
{
    GSUser * contact = [_arrayOfContacts objectAtIndex:contactIndex];
    
    //[self requestImageForContactByContact:contact loadToImageView:imageView];
    [self requestImageForContactByContact:contact onComplete:completitionBlock];
}

#pragma mark - Settings

-(BOOL) loadSettings {
    NSUserDefaults * settings = [NSUserDefaults standardUserDefaults];
    
    //self.currentUser.tokenAgile = [settings objectForKey:Agile_Token_Key];
    //self.currentUser.sessionID = [NSString stringWithFormat:@"%@",[settings objectForKey:Session_Id_Key]];
    
    NSNumber * isFirstRun = [settings objectForKey:@"IsFirstRun"];
    
    if (isFirstRun) {
        self.isFirstRun = NO;
    } else {
        self.isFirstRun = YES;
    }
    
    return YES;
}

-(BOOL) saveSettings {
    NSUserDefaults * settings = [NSUserDefaults standardUserDefaults];
    
    //[settings setObject:self.currentUser.tokenAgile  forKey:Agile_Token_Key];
    //[settings setObject:self.currentUser.sessionID forKey:Session_Id_Key];
    
    [settings setObject:[NSNumber numberWithBool:YES] forKey:@"IsFirstRun"];
    
    [settings synchronize];

    return YES;
}

-(BOOL) clearSettings {
    NSUserDefaults * settings = [NSUserDefaults standardUserDefaults];
    
    [settings setObject:nil forKey:Agile_Token_Key];
    [settings setObject:nil forKey:Session_Id_Key];
    
    [settings synchronize];
    
    return YES;
}

#pragma mark - Login User

-(BOOL) checkUserLogin {
    if ([self.currentUser.tokenAgile length]==0) {
        return NO;
    } else {
        return YES;
    }
   
}

-(void) checkUserEmail:(NSString*) eMail
            onComplete:(void(^)(BOOL result, NSString * errorString)) completitionBlock {
    
    [[SGSServerManager sharedManager]
     checkEmail: eMail
     onSuccess:^(BOOL result) {
         
         GSLog(GSLogInfo, @"[checkUserEmail] Check email %@. Exists: %d", [NSNumber numberWithBool:result]);
         
         NSString * errorString = [NSString string];
         
         if (result) {
             errorString = Email_Is_In_Use_Message;
         }
         
         if (completitionBlock) {
             completitionBlock(result, errorString);
         }
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"[loginUserWithEmail] Failed to login user. Error %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO, [error domain]);
         }
     }];
}


-(void) loginUserWithEmail:(NSString*) eMail
                  password:(NSString*) password
                onComplete:(void(^)(BOOL result, NSString * errorString)) completitionBlock
{
    __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     loginUser: eMail
     password: password
     socialType: GSEmailLoginType
     deviceToken: self.deviceToken
     onSuccess:^(NSDictionary * userInfo, NSString * errorString) {
         
         GSLog(GSLogInfo, @"[loginUserWithEmail] loginUser request success. UserInfo dict: %@", userInfo);
         
         __strong typeof(self)self = weakSelf;
         
         if (userInfo) {

             @synchronized(self.currentUser) {
                 self.currentUser.tokenAgile  = [userInfo objectForKey:userTokenKey];
                 self.currentUser.sessionID = [NSString stringWithFormat:@"%@",[userInfo objectForKey:userSessionIDKey]];
                 self.currentUser.isSynchronized = [NSNumber numberWithBool:NO];
                 self.currentUser.isRegistered = [NSNumber numberWithBool:YES];
                 self.currentUser.isTemp = [NSNumber numberWithBool:NO];
                 
                 if ([self.currentUser.tokenAgile isKindOfClass:[NSNull class]]) {
                     self.currentUser.tokenAgile = nil;
                 }
             }
             
             if (self.currentUser.tokenAgile) {
                 [self saveLocalDB];
                 
                 GSLog(GSLogInfo, @"[loginUserWithEmail] loginUser success. User token: %@, Session ID: %@", self.currentUser.tokenAgile, self.currentUser.sessionID);
                 
                 [self loadMyProfileInfoFromServer];
                 
                 if (completitionBlock) {
                     completitionBlock(YES, nil);
                 }
                 
             } else {
                 
                 GSLog(GSLogWarning, @"[loginUserWithEmail] loginUser returned token is null");
                 
                 if (completitionBlock) {
                     completitionBlock(NO, @"There are some problems on server. Token is null");
                 }
             }
         } else {
             GSLog(GSLogWarning, @"[loginUserWithEmail] loginUser returned result is null");
             
             completitionBlock(NO, errorString);

         }
         
         [[NSNotificationCenter defaultCenter] postNotificationName:profileDidUpdateNotification object:nil];
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"[loginUserWithEmail] Failed to login user. Error %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO, [error localizedDescription]);
         }
         
         [[NSNotificationCenter defaultCenter] postNotificationName:profileDidNotUpdateNotification object:nil];
     }];
}

-(void) loginUserWithSocial:(NSString*) eMail
                 socialType:(NSInteger) socialType
                 onComplete:(void(^)(BOOL result, NSError * error)) completitionBlock {
    
    __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     loginUser: eMail
     password: nil
     socialType: (GSLoginType)socialType
     deviceToken: self.deviceToken
     onSuccess:^(NSDictionary * userInfo, NSString * errorString) {
         
         GSLog(GSLogInfo, @"[loginUserWithSocial] loginUser request success. UserInfo dict: %@", userInfo);
         
         __strong typeof(self)self = weakSelf;
         
         if ((![userInfo isKindOfClass:[NSNull class]]) && (userInfo)) {
             
             @synchronized(self.currentUser) {
                 self.currentUser.tokenAgile  = [userInfo objectForKey:userTokenKey];
                 self.currentUser.sessionID = [NSString stringWithFormat:@"%@",[userInfo objectForKey:userSessionIDKey]];
                 self.currentUser.isSynchronized = [NSNumber numberWithBool:NO];
                 self.currentUser.isRegistered = [NSNumber numberWithBool:YES];
                 self.currentUser.isTemp = [NSNumber numberWithBool:NO];
                 self.currentUser.socialType = [NSNumber numberWithInteger:socialType];
                 
                 if ([self.currentUser.tokenAgile isKindOfClass:[NSNull class]]) {
                     self.currentUser.tokenAgile = nil;
                 }
             }
             
             if (self.currentUser.tokenAgile) {

                 [self loadMyProfileInfoFromServer];
                 
                 [self saveLocalDB];
                 
                 GSLog(GSLogInfo, @"[loginUserWithSocial] loginUser success. User token: %@, Session ID: %@", self.currentUser.tokenAgile, self.currentUser.sessionID);
                 
                 if (completitionBlock) {
                     completitionBlock(YES, nil);
                 }
                 
             } else {
                 
                 GSLog(GSLogWarning, @"[loginUserWithSocial] loginUser returned token is null");
                 
                 if (completitionBlock) {
                     completitionBlock(NO, nil);
                 }
             }
         } else {
             GSLog(GSLogWarning, @"[loginUserWithSocial] loginUser returned result is null");
             
             if (completitionBlock) {
                 completitionBlock(NO, nil);
             }
         }
         
         [[NSNotificationCenter defaultCenter] postNotificationName:profileDidUpdateNotification object:nil];
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"[loginUserWithSocial] Failed to login user. Error %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO, error);
         }
         
         [[NSNotificationCenter defaultCenter] postNotificationName:profileDidNotUpdateNotification object:nil];
     }];
}


-(void) createUserWithEmail:(NSString*) eMail
                   password:(NSString*) password
                 onComplete:(void(^)(BOOL result, NSError * error)) completitionBlock
{
    __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     createUser: eMail
     password: password
     socialType: GSEmailLoginType
     deviceToken: self.deviceToken
     onSuccess:^(NSDictionary * userInfo) {
         
         GSLog(GSLogInfo, @"[createUserWithEmail] createUser request success. UserInfo dict: %@", userInfo);
         
         __strong typeof(self)self = weakSelf;
         
         if ((![userInfo isKindOfClass:[NSNull class]]) && (userInfo)) {
             
             @synchronized(self.currentUser) {
                 self.currentUser.tokenAgile = [userInfo objectForKey:userTokenKey];
                 self.currentUser.sessionID = [NSString stringWithFormat:@"%@",[userInfo objectForKey:userSessionIDKey]];
                 self.currentUser.eMail = eMail;
                 self.currentUser.password = password;
                 self.currentUser.isSynchronized = [NSNumber numberWithBool:YES];
                 self.currentUser.isRegistered = [NSNumber numberWithBool:YES];
                 self.currentUser.isTemp = [NSNumber numberWithBool:NO];
                 
                 if ([self.currentUser.tokenAgile isKindOfClass:[NSNull class]]) {
                     self.currentUser.tokenAgile = nil;
                 }
             } //@synchronized(_currentUser)
             
             if (self.currentUser.tokenAgile) {
                 [self saveSettings];
                 
                 GSLog(GSLogInfo, @"[createUserWithEmail] createUser success. User token: %@, Session ID: %@", self.currentUser.tokenAgile, self.currentUser.sessionID);
                 
                 if (completitionBlock) {
                     completitionBlock(YES, nil);
                 }
             } else {
                 GSLog(GSLogWarning, @"[createUserWithEmail] createUser returned token is null");
                 
                 if (completitionBlock) {
                     completitionBlock(NO, nil);
                 }
             }
         } else {
             GSLog(GSLogWarning, @"[createUserWithEmail] createUser returned result is null");
             
             if (completitionBlock) {
                 completitionBlock(NO, nil);
             }
         }
         
         [[NSNotificationCenter defaultCenter] postNotificationName:profileDidUpdateNotification object:nil];
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"[createUserWithEmail] Failed to create user. Error %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO, error);
         }
         
         [[NSNotificationCenter defaultCenter] postNotificationName:profileDidNotUpdateNotification object:nil];
     }];
}


-(void) createUserWithSocial:(NSString*) eMail
                  socialType:(NSInteger) socialType
                  onComplete:(void(^)(BOOL result, NSError * error)) completitionBlock
{
   __weak __typeof(self)weakSelf = self;
    
    [[SGSServerManager sharedManager]
     createUser: eMail
     password: nil
     socialType: (GSLoginType) socialType
     deviceToken: self.deviceToken
     onSuccess:^(NSDictionary * userInfo) {
         
         GSLog(GSLogInfo, @"[createUserWithSocial] createUser request success. UserInfo dict: %@", userInfo);
         
         __strong typeof(self)self = weakSelf;
         
         if ((![userInfo isKindOfClass:[NSNull class]]) && (userInfo)) {
             
             @synchronized(self.currentUser) {

                 self.currentUser.tokenAgile = [userInfo objectForKey:userTokenKey];
                 self.currentUser.sessionID = [NSString stringWithFormat:@"%@",[userInfo objectForKey:userSessionIDKey]];
                 self.currentUser.socialType = [NSNumber numberWithInteger:socialType];
                 self.currentUser.eMail = eMail;
                 self.currentUser.isSynchronized = [NSNumber numberWithBool:YES];
                 self.currentUser.isRegistered = [NSNumber numberWithBool:YES];
                 self.currentUser.isTemp = [NSNumber numberWithBool:NO];

                 if ([self.currentUser.tokenAgile isKindOfClass:[NSNull class]]) {
                     self.currentUser.tokenAgile = nil;
                 }
             } //@synchronized(_currentUser)
             
             if (self.currentUser.tokenAgile) {
                 [self saveSettings];
                 
                 GSLog(GSLogInfo, @"[createUserWithSocial] createUser success. User token: %@, Session ID: %@", self.currentUser.tokenAgile, self.currentUser.sessionID);
                 
                 if (completitionBlock) {
                     completitionBlock(YES, nil);
                 }
             } else {
                 
                 GSLog(GSLogWarning, @"[createUserWithSocial] createUser returned token is null");
                 
                 if (completitionBlock) {
                     completitionBlock(NO, nil);
                 }
             }
         } else {
             GSLog(GSLogWarning, @"[createUserWithSocial] createUser returned result is null");
             
             if (completitionBlock) {
                 completitionBlock(NO, nil);
             }
         }
         
         [[NSNotificationCenter defaultCenter] postNotificationName:profileDidUpdateNotification object:nil];
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"[createUserWithSocial] Failed to create user. Error %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO, error);
         }
         
         [[NSNotificationCenter defaultCenter] postNotificationName:profileDidNotUpdateNotification object:nil];
     }];
}

#pragma mark - Connections

-(void) sendConnectionRequest:(NSString*) toUserID
                   onComplete:(void(^)(BOOL result, NSError*)) completitionBlock
{
    [[SGSServerManager sharedManager]
     sendConnection: self.currentUser.tokenAgile
     toUserID: [NSNumber numberWithInteger:[toUserID integerValue]]
     onSuccess:^() {
         
         GSLog(GSLogInfo, @"[sendConnectionRequest] success");
         
         if (completitionBlock) {
             completitionBlock(YES, nil);
         }
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         
         GSLog(GSLogError, @"[sendConnectionRequest] failed. Error %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO, error);
         }
         
     }];
}

-(void) checkConnectionRequest:(void(^)(BOOL result)) completitionBlock {
    
    if (![self.checkConnectionLock tryLock]) return;
    
    __weak __typeof(self) weakSelf = self;
    
    [[SGSServerManager sharedManager]
     checkConnection: self.currentUser.tokenAgile
     onSuccess:^(NSDictionary * arrayWithDictionaryOfRequestors) {
         GSLog(GSLogInfo, @"[checkConnectionRequest] success");
         
         __strong typeof(self)self = weakSelf;
         
         if ((arrayWithDictionaryOfRequestors) && (![arrayWithDictionaryOfRequestors isKindOfClass:[NSNull class]])) {
             
             @synchronized(self) {
                 GSUser * requester = [self createContactInLocalDB];
                 
                 requester.isMyProfile = [NSNumber numberWithBool:NO];
                 requester.isTemp = [NSNumber numberWithBool:YES];
                 
                 [requester completeWithServerResponse:arrayWithDictionaryOfRequestors];
                 requester.isChanged = [NSNumber numberWithBool:NO];
                 self.currentRequester = requester;
                 
             }
             [self saveLocalDB];
             //[self loadContactsFromLocalDB];
             
             if (completitionBlock) {
                 completitionBlock(YES);
             }
         } else {
             
             [self.checkConnectionLock unlock]; // No need to wait for reply
             
             if (completitionBlock) {
                 completitionBlock(NO);
             }
         }
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"[checkConnectionRequest] failed. Error %@", error);
         
         __strong typeof(self)self = weakSelf;
         
         [self.checkConnectionLock unlock]; // No need to wait for reply
         
         if (completitionBlock) {
             completitionBlock(NO);
         }
     }];
}

-(void) replyForConnectionRequest:(NSString*) toUserID
                           answer:(BOOL) answer
                       onComplete:(void(^)(BOOL result)) completitionBlock
{
    [self.checkConnectionLock unlock];
    
    if (self.currentRequester) {
        [self deleteObjectInEntity:self.currentRequester];
        [self saveLocalDB];
        [self loadContactsFromLocalDB];
    }
    
    self.currentRequester = nil;
    
    __weak __typeof(self) weakSelf = self;
    
    [[SGSServerManager sharedManager]
     sendConnectionReply:self.currentUser.tokenAgile
     toUserID: [NSNumber numberWithInteger:[toUserID integerValue]]
     reply: answer
     onSuccess:^() {
         GSLog(GSLogInfo, @"[sendConnectionReply] success");
         
         __strong typeof(self)self = weakSelf;

         if (completitionBlock) {
             completitionBlock(YES);
         }
         
         if (answer) { //If we pressed "Accept" button then we need to download the accepted contact from the server
             [self synchronizeContactsStep1];
         }
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"[sendConnectionReply] failed. Error %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO);
         }
     }];
}

-(void) replyForReplyForConnectionRequest {
   [self synchronizeContactsStep1];
}

-(void) deleteContact:(GSUser*) contact
           onComplete:(void(^)(BOOL result, NSError * error)) completitionBlock {
    
    contact.isdeleted = [NSNumber numberWithBool:YES];
    [self saveLocalDB];
    //[self loadContactsFromLocalDB];
    
    if (completitionBlock) {
        completitionBlock(YES, nil); // No need to wait while the contact will be deleted from the server
    }
    
    __weak __typeof(self) weakSelf = self;
    
    [[SGSServerManager sharedManager]
     deleteContact: self.currentUser.tokenAgile
     userID: [NSNumber numberWithInteger:[contact.sessionID integerValue]]
     onSuccess:^() {
         
         GSLog(GSLogInfo, @"[deleteContact] success");

         __strong typeof(self)self = weakSelf;

         [self deleteObjectInEntity:contact];
         [self saveLocalDB];
         [self loadContactsFromLocalDB];
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         
         GSLog(GSLogError, @"[deleteContact] failed. Error %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO, error);
         }
         
     }];
}


#pragma mark - Notification

-(void) sendNotification:(NSString*) notification {
    [[NSNotificationCenter defaultCenter] postNotificationName:notification object:nil];
}

#pragma mark - Events

-(NSUInteger) eventsCount {
   return [self.arrayOfEvents count];
}

-(GSEvent *) eventByIndex:(NSUInteger) index {
    return [_arrayOfEvents objectAtIndex:index];
}

-(GSEvent *) eventByID:(NSNumber*) eventID {

    if ([_arrayOfEvents count]==0) {
        return nil;
    }

    NSError * error = nil;
    NSFetchRequest * request = [[NSFetchRequest alloc] init];
    
    NSEntityDescription * entity = [NSEntityDescription entityForName:@"GSEvent" inManagedObjectContext:self.defaultManagedObjectContext];
    
    NSPredicate * predicate = [NSPredicate predicateWithFormat:@"eventId = %@", eventID];
    
    [request setEntity:entity];
    [request setPredicate:predicate];
    
    NSArray * eventsArray = [self.defaultManagedObjectContext executeFetchRequest:request error:&error];
    
    if (error) {
        GSLog(GSLogError, @"eventByID Fetch request error: %@", [error localizedDescription]);
        return nil;
    } else {
        if ([eventsArray count]==0) {
            return nil;
        } else {
            return [eventsArray objectAtIndex:0];
        }
    }
}

-(void) makeEventCurrentByID:(NSNumber*) eventID {
    _currentEvent = [self eventByID:eventID];
}

-(void) makeEventCurrentByIndex:(NSUInteger) index {
    _currentEvent = [_arrayOfEvents objectAtIndex:index];
}

-(GSEvent*) currentEvent {
    return _currentEvent;
}

-(void) sendCheckInToServerForEvent:(GSEvent*) event
                         onComplete:(void(^)(BOOL result)) completitionBlock {
    
    __weak __typeof(self) weakSelf = self;
    
    [[SGSServerManager sharedManager]
     checkInOnEvent: _currentUser.tokenAgile
     eventID: event.eventId
     flag:[event.isCheckIn boolValue]
     onSuccess:^() {
         GSLog(GSLogInfo, @"sendCheckInToServerForEvent success");

         __strong typeof(self)self = weakSelf;

         @synchronized(self.arrayOfEvents) {
             event.isCheckInChanged = [NSNumber numberWithBool:NO];
         }
         
         [self saveLocalDB];

         if (completitionBlock) {
             completitionBlock(YES);
         }
         
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"sendCheckInToServerForEvent failed. Error %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO);
         }
     }];
}

-(void) checkInOnEvent:(GSEvent*) event
               checkIn:(BOOL) checkIn
            onComplete:(void(^)(BOOL result, NSString * errorMessage)) completitionBlock {
    
    if (checkIn) {
        for (GSEvent * eventInDB in _arrayOfEvents) {
            
            if ([event isEqual:eventInDB]) {
                continue;
            }
            
            if (![eventInDB.isCheckIn boolValue]) {
                continue;
            }

            int checkTimeRes = checkDateInRange(eventInDB.startTime, event.startTime, eventInDB.endTime);
            if (checkTimeRes==0) {
                if (completitionBlock) {
                    completitionBlock(NO,Event_Check_In_Same_Time_Message);
                }
                return;
            }
            
        }
    }

    
    @synchronized(_arrayOfEvents) {
        event.isCheckIn = [NSNumber numberWithBool:checkIn];
        event.isCheckInChanged = [NSNumber numberWithBool:YES];
    }
    
    [self saveLocalDB];
    
    [self sendCheckInToServerForEvent:event onComplete:^(BOOL result) {
        if (completitionBlock) {
            completitionBlock(result, nil);
        }
    }];

}

-(void) sendEventRateToServer:(GSEvent*) event
       onComplete:(void(^)(BOOL result)) completitionBlock {
    
    __weak __typeof(self) weakSelf = self;
    
    [[SGSServerManager sharedManager]
     rateEvent: _currentUser.tokenAgile
     eventID: event.eventId
     rating:event.myRating
     onSuccess:^(NSDictionary * eventInfo) {
         GSLog(GSLogInfo, @"sendEventRateToServer success");

         __strong typeof(self)self = weakSelf;

         @synchronized(self.arrayOfEvents) {
             event.avgRating = [eventInfo objectForKey:eventAvgRaitingKey];
             event.isRatingChanged = [NSNumber numberWithBool:NO];
         }
         
         [self saveLocalDB];
         
         if (completitionBlock) {
             completitionBlock(YES);
         }
     } onFailure:^(NSError *error, NSInteger statusCode) {
         GSLog(GSLogError, @"sendEventRateToServer failed. Error %@", error);
         
         if (completitionBlock) {
             completitionBlock(NO);
         }
     }];
}

-(void) rateEvent:(GSEvent*) event
           rating:(NSInteger) rating
       onComplete:(void(^)(BOOL result)) completitionBlock {

    @synchronized(_arrayOfEvents) {
        event.myRating = [NSNumber numberWithInteger:rating];
        event.isRatingChanged = [NSNumber numberWithBool:YES];
    }
    
    [self saveLocalDB];
    
    [self sendEventRateToServer:event onComplete:^(BOOL result) {
        if (completitionBlock) {
            completitionBlock(result);
        }
    }];
}


#pragma mark - Contacts

-(NSUInteger) contactsCount {
    return [self.arrayOfContacts count];
}

-(GSUser *) contactByIndex:(NSUInteger) index {
    return [_arrayOfContacts objectAtIndex:index];
}

-(GSUser *) contactBySessionID:(NSNumber*) sessionID {
    
    if ([_arrayOfContacts count]==0) {
        return nil;
    }
    
    NSError * error = nil;
    NSFetchRequest * request = [[NSFetchRequest alloc] init];
    
    NSEntityDescription * entity = [NSEntityDescription entityForName:@"GSUser" inManagedObjectContext:self.defaultManagedObjectContext];
    
    NSPredicate * predicate = [NSPredicate predicateWithFormat:@"sessionID = %@", sessionID];
    
    [request setEntity:entity];
    [request setPredicate:predicate];
    
    NSArray * usersArray = [self.defaultManagedObjectContext executeFetchRequest:request error:&error];
    
    if (error) {
        GSLog(GSLogError, @"contactBySessionID Fetch request error: %@", [error localizedDescription]);
        return nil;
    } else {
        if ([usersArray count]==0) {
            return nil;
        } else {
            return [usersArray objectAtIndex:0];
        }
    }
}

-(void) makeContactCurrentByID:(NSNumber*) contactID {
    _currentContact = [self contactBySessionID:contactID];
}

-(void) makeContactCurrentByIndex:(NSUInteger) index {
    _currentContact = [_arrayOfContacts objectAtIndex:index];
}

-(GSUser*) currentContact {
    return _currentContact;
}

-(void) saveContactNotesAndSendToServer:(GSUser*) contact notes:(NSString*) notes {

    @synchronized (_arrayOfContacts) {
        contact.notes = notes;
        contact.isChanged = [NSNumber numberWithBool:YES];
        contact.modifiedDate = [NSDate date];
    }
    
    [self saveLocalDB];
    
    [self saveUserInfoToServer:contact onComplete:nil];
}

#pragma mark - Social Networks

// GSFacebook.h
-(void) makeLoginWithFacebook:(GSUser*) currentUser
                   onComplete: (void(^)(BOOL result)) completionBlock {
    
    self.facebook = [[GSFacebook alloc] init];
    
    __weak __typeof(self) weakSelf = self;
    
    [self.facebook connectToFacebook:^(BOOL result) {
        if (result) {
            [self.facebook requestUserInfoWithImageHeight:200
                                               Width:200
                onSuccess:^(NSDictionary *userInfo) {
                    GSLog(GSLogInfo,@"FB user info: %@", userInfo);
                    
                    for (NSString * key in [userInfo allKeys]) {
                        
                        if ([key isEqualToString:@"first_name"]) {
                            currentUser.firstName = [userInfo objectForKey:key];
                        } else if ([key isEqualToString:@"last_name"]) {
                            currentUser.lastName = [userInfo objectForKey:key];
                        } else if ([key isEqualToString:@"email"]) {
                            currentUser.eMail = [userInfo objectForKey:key];
                        } else if ([key isEqualToString:userCompanyNameKey]) {
                            currentUser.company = [userInfo objectForKey:key];
                        } else if ([key isEqualToString:userTitleKey]) {
                            currentUser.title = [userInfo objectForKey:key];
                        } else if ([key isEqualToString:@"location"]) {
                            NSDictionary * locationDic = [userInfo objectForKey:key];
                            currentUser.state = [locationDic objectForKey:@"name"];
                        } else if ([key isEqualToString:userMemoKey]) {
                            currentUser.memo = [userInfo objectForKey:key];
                        } else if ([key isEqualToString:@"id"]) {
                            currentUser.socialID = [userInfo objectForKey:key];
                            currentUser.facebookId = [userInfo objectForKey:key];
                        }  else if ([key isEqualToString:@"website"]) {
                            currentUser.webSite = [userInfo objectForKey:key];
                        }
                    }
                    
                    if (completionBlock) {
                        completionBlock(YES);
                    }

                } onImageReceived:^(NSData *userImage) {

                    __strong typeof(self)self = weakSelf;
                    [self updateUserImage:userImage forUser:currentUser];
                    
                } onFailure:^(NSError *error) {
                    GSLog(GSLogError,@"Unable to get user info. Error: %@", error.localizedDescription);
                }];
        } else {
          GSLog(GSLogError,@"Unable to connect to Facebook");
        }
    }];
}

-(void) updateIdFromFacebookForCurrentUser:(void(^)(BOOL result)) completionBlock {
    
    self.facebook = [[GSFacebook alloc] init];
    
    __weak __typeof(self) weakSelf = self;
    
    [self.facebook connectToFacebook:^(BOOL result) {
        if (result) {
            [self.facebook requestUserInfo:0
               onSuccess:^(NSDictionary *userInfo) {

                   GSLog(GSLogInfo,@"FB user info: %@", userInfo);
                   
                   __strong typeof(self)self = weakSelf;

                   @synchronized (self.currentUser) {
                        self.currentUser.facebookId = [userInfo objectForKey:@"id"];
                       
                        NSString * facebookFirstName = [userInfo objectForKey:@"first_name"];
                        NSString * facebookLastName = [userInfo objectForKey:@"last_name"];
                           
                        NSMutableString * facebookFullName = [NSMutableString stringWithFormat:@""];;
                           
                        if ((facebookFirstName) && ([facebookFirstName length]>0)) {
                             [facebookFullName appendString:facebookFirstName];
                         }
                           
                        if (([facebookFullName length]>0) && (facebookLastName) && ([facebookLastName length]>0)) {
                             [facebookFullName appendString:[NSString stringWithFormat:@" "]];
                        }
                           
                        if ((facebookLastName) && ([facebookLastName length]>0)) {
                            [facebookFullName appendString:facebookLastName];
                        }
                       
                        self.currentUser.facebookName = facebookFullName;
                       
                        self.currentUser.isChanged = [NSNumber numberWithBool:YES];
                   }
                   
                   [self saveLocalDB];
             
                   if (completionBlock) {
                       completionBlock(YES);
                   }
               } onFailure:^(NSError *error) {
                   GSLog(GSLogError,@"Unable to get user info. Error: %@", error.localizedDescription);
               }];
        } else {
            GSLog(GSLogError,@"Unable to connect to Facebook");
        }
    }];
}

-(void) updateIdAndImageFromFacebookForCurrentUser:(void(^)(BOOL result)) completionBlock {
    
    self.facebook = [[GSFacebook alloc] init];
    
    __weak __typeof(self) weakSelf = self;
    
    [self.facebook connectToFacebook:^(BOOL result) {
        if (result) {
            [self.facebook requestUserInfoWithImageHeight:200
                                                    Width:200
             onSuccess:^(NSDictionary *userInfo) {
                 GSLog(GSLogInfo,@"FB user info: %@", userInfo);
                 
                 __strong typeof(self)self = weakSelf;
                 
                 @synchronized (self.currentUser) {
                     self.currentUser.facebookId = [userInfo objectForKey:@"id"];
                     
                     NSString * facebookFirstName = [userInfo objectForKey:userFirstNameKey];
                     NSString * facebookLastName = [userInfo objectForKey:userLastNameKey];
                     
                     NSMutableString * facebookFullName = [NSMutableString stringWithFormat:@""];;
                     
                     if ((facebookFirstName) && ([facebookFirstName length]>0)) {
                         [facebookFullName appendString:facebookFirstName];
                     }
                     
                     if (([facebookFullName length]>0) && (facebookLastName) && ([facebookLastName length]>0)) {
                         [facebookFullName appendString:[NSString stringWithFormat:@" "]];
                     }
                     
                     if ((facebookLastName) && ([facebookLastName length]>0)) {
                         [facebookFullName appendString:facebookLastName];
                     }
                     
                     self.currentUser.facebookName = facebookFullName;
                     
                     self.currentUser.isChanged = [NSNumber numberWithBool:YES];
                 }
                 
                 [self saveLocalDB];
                 
                 if (completionBlock) {
                     completionBlock(YES);
                 }
             } onImageReceived:^(NSData *userImage) {
                 
                 __strong typeof(self)self = weakSelf;
                 [self updateUserImage:userImage forUser:self.currentUser];
                 
             } onFailure:^(NSError *error) {
                 GSLog(GSLogError,@"Unable to get user info. Error: %@", error.localizedDescription);
             }];
        } else {
            GSLog(GSLogError,@"Unable to connect to Facebook");
        }
    }];
}

-(void) clearIdForFacebookForCurrentUser:(void(^)(BOOL result)) completionBlock {
    @synchronized (_currentUser) {
        _currentUser.facebookId = @"";
        _currentUser.facebookName = @"";
        _currentUser.isChanged = [NSNumber numberWithBool:YES];
    }
    
    [self saveLocalDB];
    
    if (completionBlock) {
        completionBlock(YES);
    }
}

-(void) showProfileInFacebookForURL:(NSString*) url
                         onComplete:(void(^)(BOOL result)) completionBlock {
    
    self.facebook = [[GSFacebook alloc] init];
    
    //__weak __typeof(self) weakSelf = self;
    
    [self.facebook connectToFacebook:^(BOOL result) {
        if (result) {
            [self.facebook  openProfilePage:url
                 onSuccess:^(NSDictionary *userInfo) {
                     
                     GSLog(GSLogInfo,@"FB user info: %@", userInfo);
                     
                 } onFailure:^(NSError *error) {
                     GSLog(GSLogError,@"Unable to get user info. Error: %@", error.localizedDescription);
                 }];
        } else {
            GSLog(GSLogError,@"Unable to connect to Facebook");
        }
    }];
}


//GSLinkedIn.h
-(void) makeLoginWithLinkedIn:(GSUser*) currentUser
                   onComplete: (void(^)(BOOL result)) completionBlock {
    
    self.linkedIn = [[GSLinkedIn alloc] init];
    
    __weak __typeof(self) weakSelf = self;
    
    [self.linkedIn connectToLinkedIn:0
           onSuccess:^{
               [self.linkedIn requestUserInfoWithImageHeight:200
                                                       Width:200
                    onSuccess:^(NSDictionary *userInfo) {
                        GSLog(GSLogInfo, @"LinkedIn UserInfo: %@", userInfo);
                        
                        for (NSString * key in [userInfo allKeys]) {
                            
                            if ([key isEqualToString:@"firstName"]) {
                                currentUser.firstName = [userInfo objectForKey:key];
                            } else if ([key isEqualToString:@"lastName"]) {
                                currentUser.lastName = [userInfo objectForKey:key];
                            } else if ([key isEqualToString:@"id"]) {
                                //currentUser.socialID = [userInfo objectForKey:key];
                                //currentUser.linkedInId = [userInfo objectForKey:key];
                            } else if ([key isEqualToString:@"publicProfileUrl"]) {
                               currentUser.linkedInId = [userInfo objectForKey:key];
                            } else if ([key isEqualToString:@"email"]) {
                                currentUser.eMail = [userInfo objectForKey:key];
                            } else if ([key isEqualToString:@"website"]) {
                                currentUser.webSite = [userInfo objectForKey:key];
                            } else if ([key isEqualToString:@"location"]) {
                                NSDictionary * dict = [userInfo objectForKey:key];
                                currentUser.state = [dict objectForKey:@"name"];
                            } else if ([key isEqualToString:@"headline"]) {
                                NSString * companyAndTitle = [userInfo objectForKey:key];
                                
                                NSArray * array = [companyAndTitle componentsSeparatedByString:@" - "];
                                
                                if ([array count] <= 1) {
                                    array = [companyAndTitle componentsSeparatedByString:@" at "];
                                }
                                
                                if ([array count]>1) {
                                    currentUser.title = [[array objectAtIndex:0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
                                    currentUser.company = [[array objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
                                } else if ([array count]==1) {
                                    currentUser.title = [array objectAtIndex:0];
                                }
                            } else if ([key isEqualToString:@"siteStandardProfileRequest"]) {
                                //NSDictionary * profileDic = [userInfo objectForKey:key];
                                //currentUser.linkedInLink = [locDic objectForKey:@"url"];
                            }
                        }
                        
                        if (completionBlock) {
                            completionBlock(YES);
                        }
                    }
                    onImageReceived:^(NSData *userImage) {

                        __strong typeof(self)self = weakSelf;
                        [self updateUserImage:userImage forUser:currentUser];
                        
                    }
                    onFailure:^(NSError *error) {
                        GSLog(GSLogError,@"Unable to get user info from LinkedIn. Error: %@", error.localizedDescription);
                    }];
           } onFailure:^(NSError *error, NSInteger statusCode) {
               GSLog(GSLogError,@"Unable to connect to LinkedIn");
           }];
}

-(void) updateIdFromLinkedInForCurrentUser:(void(^)(BOOL result)) completionBlock {
    
    self.linkedIn = [[GSLinkedIn alloc] init];
    
    __weak __typeof(self) weakSelf = self;
    
    [self.linkedIn connectToLinkedIn:0
       onSuccess:^{
           [self.linkedIn requestUserInfo:0
               onSuccess:^(NSDictionary *userInfo) {
                   GSLog(GSLogInfo, @"LinkedIn UserInfo: %@", userInfo);
                   
                   __strong typeof(self)self = weakSelf;
                   
                   @synchronized (self.currentUser) {
                       //self.currentUser.linkedInId = [userInfo objectForKey:@"id"];
                       self.currentUser.linkedInId = [userInfo objectForKey:@"publicProfileUrl"];

                       NSString * linkedInFirstName = [userInfo objectForKey:@"firstName"];
                       NSString * linkedInLastName = [userInfo objectForKey:@"lastName"];
                       
                       NSMutableString * linkedInFullName = [NSMutableString stringWithFormat:@""];;
                       
                       if ((linkedInFirstName) && ([linkedInFirstName length]>0)) {
                           [linkedInFullName appendString:linkedInFirstName];
                       }
                       
                       if (([linkedInFullName length]>0) && (linkedInLastName) && ([linkedInLastName length]>0)) {
                           [linkedInFullName appendString:[NSString stringWithFormat:@" "]];
                       }
                       
                       if ((linkedInLastName) && ([linkedInLastName length]>0)) {
                           [linkedInFullName appendString:linkedInLastName];
                       }
                       
                       self.currentUser.linkedInName = linkedInFullName;
                       
                       self.currentUser.isChanged = [NSNumber numberWithBool:YES];
                   }
                   
                   [self saveLocalDB];
                   
                   if (completionBlock) {
                       completionBlock(YES);
                   }
               }
               onFailure:^(NSError *error) {
                   GSLog(GSLogError,@"Unable to get user info from LinkedIn. Error: %@", error.localizedDescription);
               }];
       } onFailure:^(NSError *error, NSInteger statusCode) {
           GSLog(GSLogError,@"Unable to connect to LinkedIn");
       }];
}

-(void) updateIdAndImageFromLinkedInForCurrentUser:(void(^)(BOOL result)) completionBlock {
    
    self.linkedIn = [[GSLinkedIn alloc] init];
    
    __weak __typeof(self) weakSelf = self;
    
    [self.linkedIn connectToLinkedIn:0
       onSuccess:^{
           [self.linkedIn requestUserInfoWithImageHeight:200
                                                   Width:200
                onSuccess:^(NSDictionary *userInfo) {
                    GSLog(GSLogInfo, @"LinkedIn UserInfo: %@", userInfo);
                    
                    __strong typeof(self)self = weakSelf;
                    
                    @synchronized (self.currentUser) {
                        //self.currentUser.linkedInId = [userInfo objectForKey:@"id"];
                        self.currentUser.linkedInId = [userInfo objectForKey:@"publicProfileUrl"];
                        
                        NSString * linkedInFirstName = [userInfo objectForKey:@"firstName"];
                        NSString * linkedInLastName = [userInfo objectForKey:@"lastName"];
                        
                        NSMutableString * linkedInFullName = [NSMutableString stringWithFormat:@""];;
                        
                        if ((linkedInFirstName) && ([linkedInFirstName length]>0)) {
                            [linkedInFullName appendString:linkedInFirstName];
                        }
                        
                        if (([linkedInFullName length]>0) && (linkedInLastName) && ([linkedInLastName length]>0)) {
                            [linkedInFullName appendString:[NSString stringWithFormat:@" "]];
                        }
                        
                        if ((linkedInLastName) && ([linkedInLastName length]>0)) {
                            [linkedInFullName appendString:linkedInLastName];
                        }
                        
                        self.currentUser.linkedInName = linkedInFullName;
                        
                        self.currentUser.isChanged = [NSNumber numberWithBool:YES];
                    }
                    
                    [self saveLocalDB];
                    
                    if (completionBlock) {
                        completionBlock(YES);
                    }
                }
                onImageReceived:^(NSData *userImage) {

                    __strong typeof(self)self = weakSelf;
                    [self updateUserImage:userImage forUser:_currentUser];
                    
                }
                onFailure:^(NSError *error) {
                    GSLog(GSLogError,@"Unable to get user info from LinkedIn. Error: %@", error.localizedDescription);
                }];
       } onFailure:^(NSError *error, NSInteger statusCode) {
           GSLog(GSLogError,@"Unable to connect to LinkedIn");
       }];
}

-(void) clearIdForLinkedInForCurrentUser:(void(^)(BOOL result)) completionBlock {
    
    @synchronized (_currentUser) {
        _currentUser.linkedInId = @"";
        _currentUser.linkedInName = @"";
        _currentUser.isChanged = [NSNumber numberWithBool:YES];
    }
    
    [self saveLocalDB];

    if (completionBlock) {
        completionBlock(YES);
    }
}

-(void) makeLoginWithGooglePlus:(GSUser*) currentUser
                   onComplete: (void(^)(BOOL result)) completionBlock {
    
    self.googlePlus = [[GSGooglePlus alloc] init];
    
    __weak __typeof(self) weakSelf = self;
    
    [self.googlePlus loginToGooglePlus:0
         onSuccess:^() {
             [self.googlePlus requestUserInfoWithImageHeight:200
                       Width:200
                   onSuccess:^(NSDictionary *userInfo) {

                       GSLog(GSLogInfo, @"GooglePlus authenticated successfully: %@", userInfo);
                       
                       for (NSString * key in [userInfo allKeys]) {
                           
                           if ([key isEqualToString:userFirstNameKey]) {
                               currentUser.firstName = [userInfo objectForKey:key];
                           } else if ([key isEqualToString:userLastNameKey]) {
                               currentUser.lastName = [userInfo objectForKey:key];
                           } else if ([key isEqualToString:userEmailKey]) {
                               currentUser.eMail = [userInfo objectForKey:key];
                           } else if ([key isEqualToString:userCompanyNameKey]) {
                               currentUser.company = [userInfo objectForKey:key];
                           } else if ([key isEqualToString:userTitleKey]) {
                               currentUser.title = [userInfo objectForKey:key];
                           } else if ([key isEqualToString:userStateKey]) {
                               currentUser.state = [userInfo objectForKey:key];
                           } else if ([key isEqualToString:userMemoKey]) {
                               currentUser.memo = [userInfo objectForKey:key];
                           } else if ([key isEqualToString:userSocialIdKey]) {
                               currentUser.socialID = [userInfo objectForKey:key];
                               currentUser.googlePlusId = [userInfo objectForKey:key];
                           }
                       }
                       
                       if (completionBlock) {
                           completionBlock(YES);
                       }

                   } onImageReceived:^(NSData *userImage) {
                       
                       __strong typeof(self)self = weakSelf;
                       [self updateUserImage:userImage forUser:currentUser];
                       
                   } onFailure:^(NSError *error) {
                       GSLog(GSLogError, @"GooglePlus authentication error: %@", error);
                       if (completionBlock) {
                           completionBlock(NO);
                       }
                   }];

         }
         onFailure:^(NSError *error) {
             GSLog(GSLogError, @"GooglePlus authentication error: %@", error);
             if (completionBlock) {
                 completionBlock(NO);
             }
         }];
}

-(void) updateIdFromGooglePlusForCurrentUser:(void(^)(BOOL result)) completionBlock {

    self.googlePlus = [[GSGooglePlus alloc] init];
    
    __weak __typeof(self) weakSelf = self;
    
    [self.googlePlus loginToGooglePlus:0
         onSuccess:^() {
             GSLog(GSLogInfo, @"GooglePlus authenticated successfully");
             [self.googlePlus requestUserInfo:0
                onSuccess:^(NSDictionary *userInfo) {
                    GSLog(GSLogInfo, @"GooglePlus received data: %@", userInfo);
                    
                    __strong typeof(self)self = weakSelf;
                    
                    @synchronized (self.currentUser) {
                        self.currentUser.googlePlusId = [userInfo objectForKey:userSocialIdKey];
                        
                        NSString * googleFirstName = [userInfo objectForKey:userFirstNameKey];
                        NSString * googleLastName = [userInfo objectForKey:userLastNameKey];
                        
                        NSMutableString * googleFullName = [NSMutableString stringWithFormat:@""];;
                        
                        if ((googleFirstName) && ([googleFirstName length]>0)) {
                            [googleFullName appendString:googleFirstName];
                        }
                        
                        if (([googleFullName length]>0) && (googleLastName) && ([googleLastName length]>0)) {
                            [googleFullName appendString:[NSString stringWithFormat:@" "]];
                        }
                        
                        if ((googleLastName) && ([googleLastName length]>0)) {
                            [googleFullName appendString:googleLastName];
                        }

                        self.currentUser.googlePlusName = googleFullName;
                        
                        self.currentUser.isChanged = [NSNumber numberWithBool:YES];
                    }
                    
                    [self saveLocalDB];
                    
                    if (completionBlock) {
                        completionBlock(YES);
                    }
                }
                onFailure:^(NSError *error) {
                    GSLog(GSLogError, @"GooglePlus received data from goole error: %@", error);
                    if (completionBlock) {
                        completionBlock(NO);
                    }
                }];
             
         }
         onFailure:^(NSError *error) {
             GSLog(GSLogError, @"GooglePlus authentication error: %@", error);
             if (completionBlock) {
                 completionBlock(NO);
             }
         }];
}

-(void) updateIdAndImageFromGooglePlusForCurrentUser:(void(^)(BOOL result)) completionBlock {
    
    self.googlePlus = [[GSGooglePlus alloc] init];

    __weak __typeof(self) weakSelf = self;

    [self.googlePlus loginToGooglePlus:0
         onSuccess:^() {
             GSLog(GSLogInfo, @"GooglePlus authenticated successfully");
             [self.googlePlus requestUserInfoWithImageHeight:200
                                                       Width:200
                onSuccess:^(NSDictionary *userInfo) {

                    GSLog(GSLogInfo, @"GooglePlus received data: %@", userInfo);
                    
                    __strong typeof(self)self = weakSelf;
                    
                    @synchronized (self.currentUser) {
                        self.currentUser.googlePlusId = [userInfo objectForKey:userSocialIdKey];
                        
                        NSString * googleFirstName = [userInfo objectForKey:userFirstNameKey];
                        NSString * googleLastName = [userInfo objectForKey:userLastNameKey];
                        
                        NSMutableString * googleFullName = [NSMutableString stringWithFormat:@""];
                        
                        if ((googleFirstName) && ([googleFirstName length]>0)) {
                            [googleFullName appendString:googleFirstName];
                        }
                        
                        if (([googleFullName length]>0) && (googleLastName) && ([googleLastName length]>0)) {
                            [googleFullName appendString:[NSString stringWithFormat:@" "]];
                        }
                        
                        if ((googleLastName) && ([googleLastName length]>0)) {
                            [googleFullName appendString:googleLastName];
                        }
                        
                        self.currentUser.googlePlusName = googleFullName;
                        
                        self.currentUser.isChanged = [NSNumber numberWithBool:YES];
                    }
                    
                    [self saveLocalDB];
                    
                    if (completionBlock) {
                        completionBlock(YES);
                    }
                } onImageReceived:^(NSData *userImage) {

                    __strong typeof(self)self = weakSelf;
                    [self updateUserImage:userImage forUser:self.currentUser];
                    
                }
                onFailure:^(NSError *error) {
                    GSLog(GSLogError, @"GooglePlus received data from goole error: %@", error);
                    if (completionBlock) {
                        completionBlock(NO);
                    }
                }];
         }
         onFailure:^(NSError *error) {
             GSLog(GSLogError, @"GooglePlus authentication error: %@", error);
             if (completionBlock) {
                 completionBlock(NO);
             }
         }];
}

-(void) clearIdForGooglePlusForCurrentUser:(void(^)(BOOL result)) completionBlock {
    
    @synchronized (_currentUser) {
        _currentUser.googlePlusId = @"";
        _currentUser.googlePlusName = @"";
        _currentUser.isChanged = [NSNumber numberWithBool:YES];
    }
    
    [self saveLocalDB];
    
    if (completionBlock) {
        completionBlock(YES);
    }
}

#pragma mark - Device Token

-(void) saveDeviceToken:(NSData*) deviceToken {
    NSCharacterSet *charactersToRemove = [NSCharacterSet characterSetWithCharactersInString:@"<> "];
    self.deviceToken = [[[NSString stringWithFormat:@"%@",deviceToken] componentsSeparatedByCharactersInSet:charactersToRemove] componentsJoinedByString: @""];
}

-(void) saveDeviceTokenError:(NSString*) error {
    self.deviceToken = [NSString stringWithFormat:@"\' %@ \'",error];
}

#pragma mark - Spinners


-(void) startSpinner {
    SGSAppDelegate * appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate startSpinner];
    
}

-(void) stopSpinner {
    SGSAppDelegate * appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate stopSpinner];
}

-(void) startStatusSpinner {
    SGSAppDelegate * appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate startStatusBarSpinner];
    
}

-(void) stopStatusSpinner {
    SGSAppDelegate * appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate stopStatusBarSpinner];
}


#pragma mark - Core Data stack

- (void)saveContext
{
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.defaultManagedObjectContext;
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            GSLog(GSLogError, @"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}


// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)defaultManagedObjectContext
{
    if (_defaultManagedObjectContext != nil) {
        return _defaultManagedObjectContext;
    }
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        //_defaultManagedObjectContext = [[NSManagedObjectContext alloc] init];
        _defaultManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_defaultManagedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _defaultManagedObjectContext;
}

- (NSManagedObjectContext *) managedObjectContextForCurrentThread;
{
	if ([NSThread isMainThread])
	{
		GSLog(GSLogInfo, @"Main Thread Context");
        return [self defaultManagedObjectContext];
	}
	else
	{
        GSLog(GSLogInfo, @"Child Thread Context: %@",[[NSThread currentThread] description]);

        NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
        [context setParentContext:self.defaultManagedObjectContext];
        return context;
	}
}

// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"RapidSquare" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"AgileNYC.sqlite"];
    
    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        
        [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
        
        GSLog(GSLogError,@"persistentStoreCoordinator Unresolved error %@, %@", error, [error userInfo]);
        
        [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];
    }
    
    return _persistentStoreCoordinator;
}

#pragma mark - Application's Documents directory

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

@end
